- From: Kentaro Hara <haraken@chromium.org>
- Date: Wed, 26 Oct 2011 12:42:41 +0900
- To: public-webapps@w3.org
- Cc: Dominic Cooney <dominicc@chromium.org>
- Message-ID: <CABg10jzMZAaZANP+_-MBYY_5FFJLoMNnB2FxYpPHTrNYEnS4sA@mail.gmail.com>
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 <http://xxx.com/xxx.png>"} ) ] ); document.body.appendChild(button); is equivalent to the following HTML: <button disabled> Click Me!! <img src = "http://example.com/xxx.png <http://xxx.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)
Received on Wednesday, 26 October 2011 03:43:47 UTC