W3C home > Mailing lists > Public > public-webapps@w3.org > October to December 2011

Re: A proposal for Element constructors

From: Kentaro Hara <haraken@chromium.org>
Date: Thu, 27 Oct 2011 14:34:19 +0900
Message-ID: <CABg10jwigwjb-HWTzDUbLU=Q6MdMrxKbzucGub-PFj+TfLpCKw@mail.gmail.com>
To: Rick Waldron <waldron.rick@gmail.com>
Cc: public-webapps@w3.org, Dominic Cooney <dominicc@chromium.org>
Thanks, all!

Adam:
> Another solution to that "more than one tag per interface" problem is> to introduce subclasses of those interfaces for each tag.
I agree. "new HTMLInsElement()" is more consistent with other Element
constructors than "new HTMLModificationElement( {tagName: 'ins'} )".


Rick:
> As a developer that writes JavaScript every single day,
> the sheer amount of typed characters required for using
> the constructors in this proposal is enough for me to avoid it at all costs.

I think that

    new HTMLButtonElement( {disabled: true},
                                                [new Text("Click Me!!"),
                                                 new Image( {src:
"http://example.com/xxx.png"} ) ] );

is as simple as you write it using Element.create(), and is much
simpler and more readable than you write it using button.disabled and
button.appendChild(). In any way, consistency with other DOM
constructable objects and sub-typing are the advantages for letting
Elements have constructors, as I described in the first mail.


>> (c) How to specify child Elements in the constructor argument
>> Possible approaches are as follows:
>> (c-1) As variable arguments, e.g. new HTMLButtonElement( { ... }, child1, child2, child3, ...)
>> (c-2) As an array, e.g. new HTMLButtonElement( { ... }, [child1, child2, child3] )
>> (c-3) As variable arguments. We expand arguments of type array. e.g. new HTMLButtonElement( { ... }, child1, [child2, child3], child4, ...) is equivalent to new HTMLButtonElement({ ... }, child1, child2, child3, child4, ...)

Alex Russell told me a better idea, just making the children array an
optional "childNodes" or "children" property of the dictionary, like
this:

    new HTMLButtonElement( { disable: true, childNodes: [child1,
child2, ...] } )

We now do not need the second argument. I feel that just "a dictionary
of Element properties" in the first argument is easy-to-understand.



2011年10月27日8:07 Rick Waldron <waldron.rick@gmail.com>:
>
> As a developer that writes JavaScript every single day, the sheer amount of typed characters required for using the constructors in this proposal is enough for me to avoid it at all costs.
>
>
> On Tue, Oct 25, 2011 at 11:42 PM, Kentaro Hara <haraken@chromium.org> wrote:
>>
>> Hi folks,
>> * Background *
>> I have been working on making DOM objects look and feel more like ordinary JavaScript objects. I implemented Event constructors  [1], for example.
>>
>> * Proposal *
>> Element.create() has proposed and been under discussion [2]. Besides Element.create(), I propose constructors for Elements, like "new HTMLButtonElement(...)", "new HTMLDivElement(...)" and "new HTMLVideoElement(...)". I think that both Element.create() *and* Element constructors are useful.
>> For example,
>>     var button = new HTMLButtonElement( {disabled: true},
>>                                                                         [new TextNode("Click Me!!"),
>>                                                                          new Image( {src: "http://example.com/xxx.png"} ) ] );
>>     document.body.appendChild(button);
>> is equivalent to the following HTML:
>>     <button disabled>
>>     Click Me!!
>>     <img src = "http://example.com/xxx.png" />
>>     </button>
>> As shown in above, the constructor has two arguments. The first one is a dictionary of Element properties. The second one is an array of Nodes, which become child nodes of the Element.
>>
>> * Advantages of Element constructors *
>> (a) Better errors when you misspell it
>> Element.create("vdieo", {src: ...} ) ==> No error; HTMLUnknownElement is created
>> new HTMLVdieoElement( {src: ...} ) ==> ReferenceError; Stack trace points you to the faulty line of code
>> (b) Consistency with other constructable DOM objects
>> e.g. new XMLHttpRequest(), new Image(), new Event(), new CustomEvent(), new MessageEvent(), ...
>> (c) Enables to subtype DOM objects in the future
>> We are planning to make DOM objects subtype-able, like this:
>>     function MyButton(text) {
>>         HTMLButtonElement.call(this);    /* (#) */
>>         this.textContent = text;
>>     }
>>     MyButton.prototype = Object.create(HTMLButtonElement.prototype, {...});
>>     var fancyButton = new MyButton("Click Me!!");
>> In order to make the line (#) work, HTMLButtonElement must have a constructor.
>>
>> * Spec examples *
>> interface [
>>     NamedConstructor(optional HTMLButtonElementInit initDict, optional NodeArray children)
>> ] HTMLButtonElement : HTMLElement {
>>     attribute boolean disabled;
>>     attribute DOMString value;
>>     ... (omitted)
>> }
>> dictionary HTMLButtonElementInit : HTMLElementInit {
>>     boolean disabled;
>>     DOMString value;
>>     ... (omitted)
>> }
>> interface [
>>     NamedConstructor(optional HTMLElementInit initDict, optional NodeArray children)
>> ] HTMLElement : Element {
>>     attribute DOMString lang;
>>     attribute DOMString className;
>>     ... (omitted)
>> }
>> dictionary HTMLElementInit : ElementInit {
>>     DOMString lang;
>>     DOMString className;
>>     ... (omitted)
>> }
>> interface Element : Node {
>>     readonly attribute DOMString tagName;
>>     ... (omitted)
>> }
>> dictionary ElementInit : NodeInit {
>>     DOMString tagName;
>>     ... (omitted)
>> }
>> interface Node {
>>     readonly attribute unsigned short nodeType;
>>     ... (omitted)
>> }
>> dictionary NodeInit {
>>     unsigned short nodeType;
>>     ... (omitted)
>> }
>>
>> * Discussion
>> (a) Element.create() *and* Element constructors?
>> I think that both are useful for the reasons that dominicc pointed out in [3]. Element.create() is good when we have a tag name in hand, like
>>     var tag = "button";
>>     Element.create(tag, { }, ...);
>> On the other hand, Element constructors have the advantages that I described above.
>> (b) How to specify properties and attributes in the dictionary argument
>> A property and an attribute are two different things [4]. A property is the thing that can be set by a setter like foo.value, and an attribute is the thing that can be set by foo.setAttribute(). A discussion has been conducted on how we can set up properties and attributes in the dictionary argument [2]. Proposed approaches in [2] are as follows:
>> (b-1) Let a constructor have two dictionaries, one for properties and the other for attributes, e.g. new HTMLButtonElement( {disabled: true}, {class: "myButton", onclick: func}, ...)
>> (b-2) Add a prefix character to attributes, e.g new HTMLButtonElement( {disabled: true, "@class": "myButton", "@onclick": func} )
>> (b-3) Introduce a reserved key "attributes", e.g new HTMLButtonElement( {disabled: true, attributes: {class: "myButton", onclick: func} } )
>> Another difficulty around attributes is how to naturally set a value of a boolean attribute in the dictionary when we have the value in hand. In case of a property, you just need to write like this:
>>     var b = new HTMLButtonElement({disabled: value});
>> However, in case of an attribute, we need to write like this:
>>     var options = {};
>>     if (value)
>>         options.disabled = "";
>>     var b = new HTMLButtonElement(options);
>> Basically, I think that we should keep the succinctness of constructors and should not introduce a new syntax just for allowing people to set attributes in the constructor. Thus, I propose that we allow only properties in the dictionary, at least at the present moment. If we found in the future that people wish to set attributes in the constructor, we can discuss the feature again then.
>> (c) How to specify child Elements in the constructor argument
>> Possible approaches are as follows:
>> (c-1) As variable arguments, e.g. new HTMLButtonElement( { ... }, child1, child2, child3, ...)
>> (c-2) As an array, e.g. new HTMLButtonElement( { ... }, [child1, child2, child3] )
>> (c-3) As variable arguments. We expand arguments of type array. e.g. new HTMLButtonElement( { ... }, child1, [child2, child3], child4, ...) is equivalent to new HTMLButtonElement({ ... }, child1, child2, child3, child4, ...)
>> I guess that (c-3) is not good for the complicated rule. I think that both (c-1) and (c-2) are possible, but I prefer (c-1). I think that implicit array expansion is not necessary, since ES6 is introducing "..." operator for array expansion [6]. In other words, you can write like this:
>>     var array = [child1, child2, child3, ......];
>>     new HTMLButtonElement({ ...... }, ...array);
>> (d) What should the owner document of the created Element be?
>> I think that we can assume that the owner document is window.document, as existing constructors (new Image(), new Audio() and new Option()) is assuming. Some cases can expect an owner document other than window.document, but they are not so common. I believe that we should focus on making most of practical use cases simpler rather than supporting specialized cases that do not really need a convenience shortcut.
>> (e) How to specify a namespace of attributes
>> I propose that we do not support namespaced attributes like "xmlns:foo", since they are rare [5]. Again, we should focus on making most of practical use cases simpler.
>> (f) How to create an unknown element that can be created by document.createElement("unknooooown")
>> Fortunately, the HTML5 spec has HTMLUnknownElement. You can create an unknown element by new HTMLUnknownElement( {tagName: "unknooooown"}, ...)
>> (g) How to handle HTMLElements that have more than one tags
>> For example, both <ins> and <del> are tags of HTMLModificationElement. Then, how does the constructor look like? I think that we can easily handle this issue by distinguishing tags using a tagName property, e.g. new HTMLModificationElement( {tagName: "ins"}, ...) or new HTMLModificationElement( {tagName: "del"}, ...)
>>
>> [1] https://bugs.webkit.org/show_bug.cgi?id=67824
>> [2] http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0537.html
>> [3] http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0707.html
>> [4] http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0666.html
>> [5] http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0721.html
>> [6] http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts (11.2.4 Argument Lists)
>>
>>
>> --
>> Kentaro Hara, Tokyo, Japan (http://haraken.info)
>



--
Kentaro Hara, Tokyo, Japan (http://haraken.info)
Received on Thursday, 27 October 2011 05:35:20 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:48 GMT