Discussion:
Element.create(): a proposal for more convenient element creation
Maciej Stachowiak
2011-08-02 01:33:45 UTC
Permalink
In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the following idea for convenient element creation:

Element.create(tagName, attributeMap, children…)

Creates an element with the specified tag, attributes, and children.

tagName - tag name as a string; by default it does "smart" selection of SVG, HTML or MathML namespace. Authors can also use an html: svg: or mathml: prefix to override these defaults. (And further, you can use xmlns in attribute map to use a custom namespace.)
attributeMap - JS object-as-dictonary or whatever dictionary type is appropriate to the language, or null for no attributes
children… - veridic parameter, can include nodes, strings, or arrays. Strings are converted to text nodes. Arrays are unpacked and treated as lists of nodes/strings. Array support is for cases where you want to have a call-site that may take a veriable-length list, with possible prefix and suffix.

Examples:

Element.create("a", {href: "http://google.com/"}, "Google"}

Element.create("p", null,
"Please consider these instructions ",
Element.create("em", {class: "intense"}, "very"),
"carefully")

Element.create('svg:a', {href: 'example.html'}, 'Click Me! Yay bad link text!');

Element.create('select', {multi: ''}, optionArray);

The default namespace mapping would be HTML, with possibly SVG or MathML elements that don't localName-collide with HTML elements mapping to their default namespace.


Why Element.create() instead of new Element()? It's a factory method. In general it returns instances of many different classes. new Foo() carries a strong expectation of returning a direct instance of Foo, not one of several subclasses.

We could also add new Text('foo') for the cases where you want to create a text node without an element around it.

Regards,
Maciej
Ian Hickson
2011-08-02 01:43:50 UTC
Permalink
On Mon, 1 Aug 2011, Maciej Stachowiak wrote:
>
> Creates an element with the specified tag, attributes, and children.
>
> tagName - tag name as a string; by default it does "smart" selection
> of SVG, HTML or MathML namespace. Authors can also use an html: svg: or
> mathml: prefix to override these defaults.

I'd suggest just always defaulting to HTML and requiring "svg:" and
"mathml:" prefixes for those namespaces. That makes code easier to read
(it's obvious what's going on, and you won't accidentally use elements
from the wrong namespace). It also makes forward-compatibility easier (we
don't have to worry about adding new elements to the list and what that
will do to future legacy UAs).


> (And further, you can use xmlns in attribute map to use a custom
> namespace.)

I wouldn't bother supporting the "xmlns" attribute in this API, but I
don't feel strongly about this.


> attributeMap - JS object-as-dictonary or whatever dictionary type is
> appropriate to the language, or null for no attributes

Optional attribute, too.


> children… - veridic parameter, can include nodes, strings, or arrays.
> Strings are converted to text nodes. Arrays are unpacked and treated as
> lists of nodes/strings. Array support is for cases where you want to
> have a call-site that may take a veriable-length list, with possible
> prefix and suffix.

Also optional.


So you could also do:

var div = Element.create('div');

--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Charles Pritchard
2011-08-02 02:05:34 UTC
Permalink
On Aug 1, 2011, at 6:43 PM, Ian Hickson <***@hixie.ch> wrote:

> On Mon, 1 Aug 2011, Maciej Stachowiak wrote:
>>
>> Creates an element with the specified tag, attributes, and children.
>>
>> tagName - tag name as a string; by default it does "smart" selection
>> of SVG, HTML or MathML namespace. Authors can also use an html: svg: or
>> mathml: prefix to override these defaults.
>
> I'd suggest just always defaulting to HTML and requiring "svg:" and
> "mathml:" prefixes for those namespaces.

Can we have it 'inherit' a parent namespace, and have chaining properties?

Element.create('div').create('svg').create('g').create('rect', {title: 'An svg rectangle in an HTML div'});
Tab Atkins Jr.
2011-08-02 03:43:28 UTC
Permalink
On Mon, Aug 1, 2011 at 7:05 PM, Charles Pritchard <***@jumis.com> wrote:
> Can we have it 'inherit' a parent namespace, and have chaining properties?
>
> Element.create('div').create('svg').create('g').create('rect', {title: 'An svg rectangle in an HTML div'});

Ooh, so .create is defined both on Element (defaults to HTML
namespace, just creates an element) and on Element.prototype (defaults
to namespace of the element, inserts as a child)? That's pretty
interesting. Presumably the new element gets inserted as a last child
of the parent.

I like it.

~TJ
João Eiras
2011-08-02 03:52:39 UTC
Permalink
On , Tab Atkins Jr. <***@gmail.com> wrote:

> On Mon, Aug 1, 2011 at 7:05 PM, Charles Pritchard <***@jumis.com> wrote:
>> Can we have it 'inherit' a parent namespace, and have chaining properties?
>>
>> Element.create('div').create('svg').create('g').create('rect', {title: 'An svg rectangle in an HTML div'});
>
> Ooh, so .create is defined both on Element (defaults to HTML
> namespace, just creates an element) and on Element.prototype (defaults
> to namespace of the element, inserts as a child)? That's pretty
> interesting. Presumably the new element gets inserted as a last child
> of the parent.
>
> I like it.
>

While the idea is interesting, "create" is a too simple name to add on something as polluted as Element.

I wonder if there is enough demand for this kind of chained coding to actually spec an API for this. I've rarely seen it being used, plus the notable exception that is jquery code.

Perhaps createChild, would be better, but then one would need to differentiate Elements from Text.
Tab Atkins Jr.
2011-08-02 04:19:02 UTC
Permalink
On Mon, Aug 1, 2011 at 8:52 PM, João Eiras <***@gmail.com> wrote:
> On , Tab Atkins Jr. <***@gmail.com> wrote:
>
>> On Mon, Aug 1, 2011 at 7:05 PM, Charles Pritchard <***@jumis.com> wrote:
>>>
>>> Can we have it 'inherit' a parent namespace, and have chaining
>>> properties?
>>>
>>> Element.create('div').create('svg').create('g').create('rect', {title:
>>> 'An svg rectangle in an HTML div'});
>>
>> Ooh, so .create is defined both on Element (defaults to HTML
>> namespace, just creates an element) and on Element.prototype (defaults
>> to namespace of the element, inserts as a child)?  That's pretty
>> interesting.  Presumably the new element gets inserted as a last child
>> of the parent.
>>
>> I like it.
>>
>
> While the idea is interesting, "create" is a too simple name to add on
> something as polluted as Element.
>
> I wonder if there is enough demand for this kind of chained coding to
> actually spec an API for this. I've rarely seen it being used, plus the
> notable exception that is jquery code.
>
> Perhaps createChild, would be better, but then one would need to
> differentiate Elements from Text.

createChild works for me too. The important part is the chaining; it
makes it slightly terser and easier to read than nesting
Element.create() calls.

~TJ
Ian Hickson
2011-08-02 04:25:15 UTC
Permalink
On Tue, 2 Aug 2011, João Eiras wrote:
>
> While the idea is interesting, "create" is a too simple name to add on
> something as polluted as Element.

Why?

I think create() is fine. It's a pretty common name for a factory or
constructor (in languages with named constructors), and having it on the
interface object makes it pretty clear what it's a factory for.

--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Charles Pritchard
2011-08-02 06:25:23 UTC
Permalink
On Aug 1, 2011, at 9:25 PM, Ian Hickson <***@hixie.ch> wrote:

> On Tue, 2 Aug 2011, João Eiras wrote:
>>
>> While the idea is interesting, "create" is a too simple name to add on
>> something as polluted as Element.
>
> Why?
>
> I think create() is fine. It's a pretty common name for a factory or
> constructor (in languages with named constructors), and having it on the
> interface object makes it pretty clear what it's a factory for.


If we can get this chained, the .attr({key:val}) syntax is handy at times, too, if / when applied to existing items.

At that point, it's very similar to the popular helpers out there, esp, jQuery. One nice aspect of that is that those package maintainers can feature test for the native model, even if it's a long-name to type. Might slightly (very slightly) improve performance in such code bases.
Maciej Stachowiak
2011-08-02 06:17:47 UTC
Permalink
On Aug 1, 2011, at 8:43 PM, Tab Atkins Jr. wrote:

> On Mon, Aug 1, 2011 at 7:05 PM, Charles Pritchard <***@jumis.com> wrote:
>> Can we have it 'inherit' a parent namespace, and have chaining properties?
>>
>> Element.create('div').create('svg').create('g').create('rect', {title: 'An svg rectangle in an HTML div'});
>
> Ooh, so .create is defined both on Element (defaults to HTML
> namespace, just creates an element) and on Element.prototype (defaults
> to namespace of the element, inserts as a child)? That's pretty
> interesting. Presumably the new element gets inserted as a last child
> of the parent.
>
> I like it.

With just the old propose you could get the same effect like this:

Element.create('div', {}, Element.create('svg', {}, Element.create('g', {}, Element.create('rect', {title: 'An svg rectangle in an HTML div'}))))

Chaining .create() is certainly less verbose. Doesn't work as well for inserting multiple children into an element though.

Regards,
Maciej
Dimitri Glazkov
2011-08-02 03:29:49 UTC
Permalink
On Mon, Aug 1, 2011 at 6:43 PM, Ian Hickson <***@hixie.ch> wrote:
> On Mon, 1 Aug 2011, Maciej Stachowiak wrote:
>>
>>    Creates an element with the specified tag, attributes, and children.
>>
>>    tagName - tag name as a string; by default it does "smart" selection
>> of SVG, HTML or MathML namespace. Authors can also use an html: svg: or
>> mathml: prefix to override these defaults.
>
> I'd suggest just always defaulting to HTML and requiring "svg:" and
> "mathml:" prefixes for those namespaces. That makes code easier to read
> (it's obvious what's going on, and you won't accidentally use elements
> from the wrong namespace). It also makes forward-compatibility easier (we
> don't have to worry about adding new elements to the list and what that
> will do to future legacy UAs).
>
>
>> (And further, you can use xmlns in attribute map to use a custom
>> namespace.)
>
> I wouldn't bother supporting the "xmlns" attribute in this API, but I
> don't feel strongly about this.
>
>
>>    attributeMap - JS object-as-dictonary or whatever dictionary type is
>> appropriate to the language, or null for no attributes
>
> Optional attribute, too.
>
>
>>    children� - veridic parameter, can include nodes, strings, or arrays.
>> Strings are converted to text nodes. Arrays are unpacked and treated as
>> lists of nodes/strings. Array support is for cases where you want to
>> have a call-site that may take a veriable-length list, with possible
>> prefix and suffix.
>
> Also optional.
>
>
> So you could also do:
>
>   var div = Element.create('div');

This is all pretty cool.

:DG<
Ryosuke Niwa
2011-08-02 01:45:46 UTC
Permalink
On Mon, Aug 1, 2011 at 6:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>
> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
> following idea for convenient element creation:
>
> Element.create(tagName, attributeMap, children…)
>

Can we alternatively extend document.createElement? Or was this
intentionally avoided to associate new elements with documents?

- Ryosuke
Ian Hickson
2011-08-02 02:53:19 UTC
Permalink
On Mon, 1 Aug 2011, Ryosuke Niwa wrote:
> On Mon, Aug 1, 2011 at 6:33 PM, Maciej Stachowiak <***@apple.com> wrote:
> >
> > In an IRC discussion with Ian Hickson and Tab Atkins, we can up with
> > the following idea for convenient element creation:
> >
> > Element.create(tagName, attributeMap, children…)
>
> Can we alternatively extend document.createElement? Or was this
> intentionally avoided to associate new elements with documents?

We could, but I'd much rather have the shorter name, personally. Having
the name be so long really makes that API unusable.

--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
João Eiras
2011-08-02 03:36:28 UTC
Permalink
On , Ian Hickson <***@hixie.ch> wrote:

> On Mon, 1 Aug 2011, Ryosuke Niwa wrote:
>> On Mon, Aug 1, 2011 at 6:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>> >
>> > In an IRC discussion with Ian Hickson and Tab Atkins, we can up with
>> > the following idea for convenient element creation:
>> >
>> > Element.create(tagName, attributeMap, children�)
>>
>> Can we alternatively extend document.createElement? Or was this
>> intentionally avoided to associate new elements with documents?
>
> We could, but I'd much rather have the shorter name, personally. Having
> the name be so long really makes that API unusable.
>

However, Nodes need a ownerDocument, and that needs to be supplied, even if optionally. Doing document.createElement implies the document, Element.create does not.
Cameron McCormack
2011-08-02 03:41:53 UTC
Permalink
On 2/08/11 3:36 PM, João Eiras wrote:
> However, Nodes need a ownerDocument, and that needs to be supplied, even
> if optionally. Doing document.createElement implies the document,
> Element.create does not.

I figure the ownerDocument would be window.document (where the window
object is the global object that window.Element lives on).
Ian Hickson
2011-08-02 04:23:42 UTC
Permalink
On Tue, 2 Aug 2011, João Eiras wrote:
>
> However, Nodes need a ownerDocument, and that needs to be supplied, even
> if optionally. Doing document.createElement implies the document,
> Element.create does not.

Just use the same document as new Image(), new Option(), or new Audio().

--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Maciej Stachowiak
2011-08-02 06:14:52 UTC
Permalink
On Aug 1, 2011, at 8:36 PM, João Eiras wrote:

> On , Ian Hickson <***@hixie.ch> wrote:
>
>> On Mon, 1 Aug 2011, Ryosuke Niwa wrote:
>>> On Mon, Aug 1, 2011 at 6:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>>> >
>>> > In an IRC discussion with Ian Hickson and Tab Atkins, we can up with
>>> > the following idea for convenient element creation:
>>> >
>>> > Element.create(tagName, attributeMap, children�)
>>>
>>> Can we alternatively extend document.createElement? Or was this
>>> intentionally avoided to associate new elements with documents?
>>
>> We could, but I'd much rather have the shorter name, personally. Having
>> the name be so long really makes that API unusable.
>>
>
> However, Nodes need a ownerDocument, and that needs to be supplied, even if optionally. Doing document.createElement implies the document, Element.create does not.

The intent is that it supplies the Window's current Document. It's true that sometimes you need to make a node for another Document, sometimes even one that lacks a namespace, but that's a sufficiently specialized case that it does not really need a convenience shortcut.

Regards,
Maciej
Jonas Sicking
2011-08-02 07:36:53 UTC
Permalink
On Mon, Aug 1, 2011 at 6:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>
> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
> following idea for convenient element creation:
> Element.create(tagName, attributeMap, children…)
>    Creates an element with the specified tag, attributes, and children.
>    tagName - tag name as a string; by default it does "smart" selection of
> SVG, HTML or MathML namespace. Authors can also use an html: svg: or mathml:
> prefix to override these defaults. (And further, you can use xmlns in
> attribute map to use a custom namespace.)
>    attributeMap - JS object-as-dictonary or whatever dictionary type is
> appropriate to the language, or null for no attributes
>    children… - veridic parameter, can include nodes, strings, or arrays.
> Strings are converted to text nodes. Arrays are unpacked and treated as
> lists of nodes/strings. Array support is for cases where you want to have a
> call-site that may take a veriable-length list, with possible prefix and
> suffix.
> Examples:
> Element.create("a", {href: "http://google.com/"}, "Google"}
> Element.create("p", null,
>                             "Please consider these instructions ",
>                             Element.create("em", {class: "intense"},
> "very"),
>                             "carefully")
> Element.create('svg:a', {href: 'example.html'}, 'Click Me! Yay bad link
> text!');
> Element.create('select', {multi: ''}, optionArray);
> The default namespace mapping would be HTML, with possibly SVG or MathML
> elements that don't localName-collide with HTML elements mapping to their
> default namespace.

I'm not sure if it's better to include the children as a var-args
list, or as an array. Certainly when typing things normally var-args
saves you the "[" and "]", but when coding, if you've built the child
list dynamically and have an array, you have to make awkward .apply
calls.

> Why Element.create() instead of new Element()? It's a factory method. In
> general it returns instances of many different classes. new Foo() carries a
> strong expectation of returning a direct instance of Foo, not one of several
> subclasses.

This doesn't explain why a factory method is better than explicit
constructors though? The above could be written as

new HTMLParagraphElement(null, "foo", ...);

However I'm not sure what to do in situations where we don't have an
explicit interface for an element, such as both <ins> and <del> both
using HTMLModElement, and the long list of elements which only use
HTMLElement as interface. cc'ing Alex Russel who is often a strong
advocate for constructors over factory functions and who might have
thought about this problem.

/ Jonas
Roland Steiner
2011-08-02 08:27:21 UTC
Permalink
On Tue, Aug 2, 2011 at 4:36 PM, Jonas Sicking <***@sicking.cc> wrote:
> This doesn't explain why a factory method is better than explicit
> constructors though? The above could be written as
>
> new HTMLParagraphElement(null, "foo", ...);

It's not a general use case, but at least when it comes to XBL-like
components, having a factory method that does all the lookup and
binding behing the scenes probably is easier to implement than hooking
a constructor (FWIW).


> However I'm not sure what to do in situations where we don't have an
> explicit interface for an element, such as both <ins> and <del> both
> using HTMLModElement, and the long list of elements which only use
> HTMLElement as interface. cc'ing Alex Russel who is often a strong
> advocate for constructors over factory functions and who might have
> thought about this problem.


Cheers,

- Roland
Dimitri Glazkov
2011-08-02 16:10:41 UTC
Permalink
On Tue, Aug 2, 2011 at 1:27 AM, Roland Steiner <***@google.com> wrote:
> On Tue, Aug 2, 2011 at 4:36 PM, Jonas Sicking <***@sicking.cc> wrote:
>> This doesn't explain why a factory method is better than explicit
>> constructors though? The above could be written as
>>
>> new HTMLParagraphElement(null, "foo", ...);
>
> It's not a general use case, but at least when it comes to XBL-like
> components, having a factory method that does all the lookup and
> binding behing the scenes probably is easier to implement than hooking
> a constructor (FWIW).

I am not sure it will be easier but it does seem that it would be more
natural to an author to write:

var foo = new FooButton();

than:

var foo = Element.create('x-foo-button').

:DG<
Ian Hickson
2011-08-02 17:37:44 UTC
Permalink
On Tue, 2 Aug 2011, Dimitri Glazkov wrote:
> On Tue, Aug 2, 2011 at 1:27 AM, Roland Steiner
> <***@google.com> wrote:
> > On Tue, Aug 2, 2011 at 4:36 PM, Jonas Sicking <***@sicking.cc>
> > wrote:
> >> This doesn't explain why a factory method is better than explicit
> >> constructors though? The above could be written as
> >>
> >> new HTMLParagraphElement(null, "foo", ...);
> >
> > It's not a general use case, but at least when it comes to XBL-like
> > components, having a factory method that does all the lookup and
> > binding behing the scenes probably is easier to implement than hooking
> > a constructor (FWIW).
>
> I am not sure it will be easier but it does seem that it would be more
> natural to an author to write:
>
> var foo = new FooButton();
>
> than:
>
> var foo = Element.create('x-foo-button').

One of the principles behind XB2L's design was graceful degradation, such
that sites would still mostly work without the bindings being applies. It
seems that if we're creating proprietary elements, that won't work. So I'm
not sure this is a use case we should be addressing.

--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Dimitri Glazkov
2011-08-02 18:02:23 UTC
Permalink
On Tue, Aug 2, 2011 at 10:37 AM, Ian Hickson <***@hixie.ch> wrote:
> On Tue, 2 Aug 2011, Dimitri Glazkov wrote:
>> On Tue, Aug 2, 2011 at 1:27 AM, Roland Steiner
>> <***@google.com> wrote:
>> > On Tue, Aug 2, 2011 at 4:36 PM, Jonas Sicking <***@sicking.cc>
>> > wrote:
>> >> This doesn't explain why a factory method is better than explicit
>> >> constructors though? The above could be written as
>> >>
>> >> new HTMLParagraphElement(null, "foo", ...);
>> >
>> > It's not a general use case, but at least when it comes to XBL-like
>> > components, having a factory method that does all the lookup and
>> > binding behing the scenes probably is easier to implement than hooking
>> > a constructor (FWIW).
>>
>> I am not sure it will be easier but it does seem that it would be more
>> natural to an author to write:
>>
>> var foo = new FooButton();
>>
>> than:
>>
>> var foo = Element.create('x-foo-button').
>
> One of the principles behind XB2L's design was graceful degradation, such
> that sites would still mostly work without the bindings being applies. It
> seems that if we're creating proprietary elements, that won't work. So I'm
> not sure this is a use case we should be addressing.

We should have a good discussion about whether we should be aiming to
subclass/extend DOM elements. Though in this particular context,
graceful degradation is a useless concept: you're building DOM
imperatively using APIs that haven't even yet even been invented
properly.

:DG<
Dimitri Glazkov
2011-08-02 16:09:54 UTC
Permalink
On Tue, Aug 2, 2011 at 1:27 AM, Roland Steiner <***@google.com> wrote:
> On Tue, Aug 2, 2011 at 4:36 PM, Jonas Sicking <***@sicking.cc> wrote:
>> This doesn't explain why a factory method is better than explicit
>> constructors though? The above could be written as
>>
>> new HTMLParagraphElement(null, "foo", ...);
>
> It's not a general use case, but at least when it comes to XBL-like
> components, having a factory method that does all the lookup and
> binding behing the scenes probably is easier to implement than hooking
> a constructor (FWIW).

I am not sure it will be easier but it does seem that it would be more
natural to an author to write:

var foo = new FooButton();

than:

var foo = Element.create('x-foo-button').

:DG<

>
>
>> However I'm not sure what to do in situations where we don't have an
>> explicit interface for an element, such as both <ins> and <del> both
>> using HTMLModElement, and the long list of elements which only use
>> HTMLElement as interface. cc'ing Alex Russel who is often a strong
>> advocate for constructors over factory functions and who might have
>> thought about this problem.
>
>
> Cheers,
>
> - Roland
>
>
Tab Atkins Jr.
2011-08-02 18:05:22 UTC
Permalink
On Tue, Aug 2, 2011 at 12:36 AM, Jonas Sicking <***@sicking.cc> wrote:
> I'm not sure if it's better to include the children as a var-args
> list, or as an array. Certainly when typing things normally var-args
> saves you the "[" and "]", but when coding, if you've built the child
> list dynamically and have an array, you have to make awkward .apply
> calls.

Read again - the idea is to auto-expand arrays.

(I don't have much of a preference between "just use an array" and
"use varargs, but expand arrays". I agree that using only varargs
without expansion would be bad.)

~TJ
Aryeh Gregor
2011-08-02 18:23:13 UTC
Permalink
On Tue, Aug 2, 2011 at 2:05 PM, Tab Atkins Jr. <***@gmail.com> wrote:
> Read again - the idea is to auto-expand arrays.
>
> (I don't have much of a preference between "just use an array" and
> "use varargs, but expand arrays".  I agree that using only varargs
> without expansion would be bad.)

I'm against "use varargs, but expand arrays" if it would make sense to
have one of the arguments be an array itself, as with Array.concat(),
since then your code is going to mysteriously fail in the varargs case
if you provide one argument that happens to be an array one time.
It's also bad if we might want to add more arguments later. But in
this case it seems fine.

On Tue, Aug 2, 2011 at 2:12 PM, Charles Pritchard <***@jumis.com> wrote:
> http://mootools.net/docs/core/Element/Element
> . . .
> // moo enables: new Element('a.className') as a shorthand.
> . . .
> http://api.jquery.com/attr/
> var myAnchor = $('<a href="http://api.jquery.com">');

These both seem interesting. Allowing class or id to be specified as
part of the first argument instead of the second one would make some
very common use-cases simpler. Often you want to create some kind of
wrapper with a static class or id, and Element.create("div.myClass")
is both immediately understandable and significantly shorter than
Element.create("div", {class: "myClass"}).

The second one seems like it might be useful as a separate API. It
could be a function that accepts a text/html string, and if the
resulting tree has a single root node, it could return that. If not,
it could return a DocumentFragment containing the result. Or maybe it
could return a DocumentFragment unconditionally for consistency, so it
would be like a much easier-to-use version of
Range.createContextualFragment. Of course, this kind of thing is bad
because it doesn't allow easy escaping, so maybe we shouldn't make it
easier.
Aryeh Gregor
2011-08-02 16:48:51 UTC
Permalink
On Mon, Aug 1, 2011 at 9:33 PM, Maciej Stachowiak <***@apple.com> wrote:
> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
> following idea for convenient element creation:
> Element.create(tagName, attributeMap, children…)
>    Creates an element with the specified tag, attributes, and children.

How does this compare to popular JS helper libraries like jQuery? It
would be useful to know what convenience APIs authors are using now
before introducing our own.
Charles Pritchard
2011-08-02 18:12:59 UTC
Permalink
On 8/2/2011 9:48 AM, Aryeh Gregor wrote:
> On Mon, Aug 1, 2011 at 9:33 PM, Maciej Stachowiak<***@apple.com> wrote:
>> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
>> following idea for convenient element creation:
>> Element.create(tagName, attributeMap, children
)
>> Creates an element with the specified tag, attributes, and children.
> How does this compare to popular JS helper libraries like jQuery? It
> would be useful to know what convenience APIs authors are using now
> before introducing our own.
>

Here are three common helper libraries.

http://mootools.net/docs/core/Element/Element
var myAnchor = new Element('a', { href: 'http://mootools.net' });
// notes: namespacing requires escaping the colon \:
// moo enables: new Element('a.className') as a shorthand.
myAnchor.set({ key: val});
// styles and events are keywords.

http://dojotoolkit.org/documentation/tutorials/1.6/dom_functions/
http://dojotoolkit.org/api/1.6/dojo
var li = dojo.create("li", { innerHTML: "Six"});
// notes: dojo allows a third and fourth param to specify parent / child
nodes and positioning.
dojo.attr(li,{key: val});

http://api.jquery.com/attr/
var myAnchor = $('<a href="http://api.jquery.com">');
myAnchor.attr({key: val});
// attr also acts as a getter, when a string is passed, and a computed
setter, when a function is used for a value.
Tab Atkins Jr.
2011-08-02 18:18:58 UTC
Permalink
On Tue, Aug 2, 2011 at 9:48 AM, Aryeh Gregor <***@aryeh.name> wrote:
> On Mon, Aug 1, 2011 at 9:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
>> following idea for convenient element creation:
>> Element.create(tagName, attributeMap, children…)
>>    Creates an element with the specified tag, attributes, and children.
>
> How does this compare to popular JS helper libraries like jQuery?  It
> would be useful to know what convenience APIs authors are using now
> before introducing our own.

jQuery's element creation is basically driven by innerHTML. That is,
to create an element, you just make a call like "$('<p class=foo>')".
I doubt that's a pattern we actually want to copy, as it's kinda
dirty, and inconvenient in some cases. (For example, to apply a bag
of properties as attributes, you have to first create the element,
then call attr() on it. You can't pass the attrs as an initial arg
without string-building.)

Prototype's element creation is almost identical to what is proposed
here, except it uses something that looks like a constructor. You
create an element with "new Element('p',{class:'foo'})". You can't
set children as part of the initial call; they have to be appended in
later calls.

MooTools is basically identical to Prototype, except that you can
additionally set listeners on the element during creation by using a
magical "events" property in the attribute bag, which takes an object
of event names and functions. This would be nice to look into adding.

Dojo uses a factory method fairly similar to what's proposed (with the
same name, even - Dojo.create()). Its first two arguments are the
tagname and an attribute bag, same as the proposal. Its next two
arguments are used to set a parent node and offset within that parent
node, for automatic DOM insertion after creation. I don't think it's
valuable to have this in the constructor, though the facilities that
the libraries offer for easier DOM insertion should definitely be
looked at separately.

I think those are the major libraries to pay attention to. It looks
like jQuery's model is probably not something we want to emulate,
while the other three libraries are almost identical to this proposal.
The one thing I suggest looking into is the ability to set listeners
on an element during creation, like MooTools allows.

~TJ
Glenn Maynard
2011-08-02 18:26:52 UTC
Permalink
On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com> wrote:

> MooTools is basically identical to Prototype, except that you can
> additionally set listeners on the element during creation by using a
> magical "events" property in the attribute bag, which takes an object
> of event names and functions. This would be nice to look into adding.
>

Is this much better than just saying eg. Element.create("a", {href: "
http://link <http://google.com/>", onclick: function(e) { ... } }, "link"}?

--
Glenn Maynard
Tab Atkins Jr.
2011-08-02 18:31:04 UTC
Permalink
On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com> wrote:
>> MooTools is basically identical to Prototype, except that you can
>> additionally set listeners on the element during creation by using a
>> magical "events" property in the attribute bag, which takes an object
>> of event names and functions.  This would be nice to look into adding.
>
> Is this much better than just saying eg. Element.create("a", {href:
> "http://link", onclick: function(e) { ... } }, "link"}?

Hmm, is everything exposed as on* attributes now? If so, then yeah,
just do that; no need to mess around with a magic property in the
attributes bag.

~TJ
Garrett Smith
2011-08-02 18:38:09 UTC
Permalink
On 8/2/11, Tab Atkins Jr. <***@gmail.com> wrote:
> On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
>> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com>
>> wrote:
>>> MooTools is basically identical to Prototype, except that you can
>>> additionally set listeners on the element during creation by using a
>>> magical "events" property in the attribute bag, which takes an object
>>> of event names and functions. This would be nice to look into adding.
>>
>> Is this much better than just saying eg. Element.create("a", {href:
>> "http://link", onclick: function(e) { ... } }, "link"}?
>
> Hmm, is everything exposed as on* attributes now? If so, then yeah,
> just do that; no need to mess around with a magic property in the
> attributes bag.
>
Good example.

Libraries that cover up the underlying APIs proliferate misconceptions
about how those underlying APIs work.

Thanks.
--
Garrett
Anne van Kesteren
2011-08-03 07:34:10 UTC
Permalink
On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
wrote:
> On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
>> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com>
>> wrote:
>>> MooTools is basically identical to Prototype, except that you can
>>> additionally set listeners on the element during creation by using a
>>> magical "events" property in the attribute bag, which takes an object
>>> of event names and functions. This would be nice to look into adding.
>>
>> Is this much better than just saying eg. Element.create("a", {href:
>> "http://link", onclick: function(e) { ... } }, "link"}?
>
> Hmm, is everything exposed as on* attributes now? If so, then yeah,
> just do that; no need to mess around with a magic property in the
> attributes bag.

This would still be magical as it is setting an IDL attribute rather than
a content attribute.


--
Anne van Kesteren
http://annevankesteren.nl/
Glenn Maynard
2011-08-03 15:46:50 UTC
Permalink
On Wed, Aug 3, 2011 at 3:34 AM, Anne van Kesteren <***@opera.com> wrote:

> On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
> wrote:
>
>> Hmm, is everything exposed as on* attributes now? If so, then yeah,
>> just do that; no need to mess around with a magic property in the
>> attributes bag.
>>
>
> This would still be magical as it is setting an IDL attribute rather than a
> content attribute.
>

What's the difference? I'd expect this:

a = Element.create("a", {href: "http://link", onclick: function(e) { },
custom: "value" }, "link");

to be essentially equivalent to

a = document.createElement("a");
a.appendChild(document.createTextNode("link"));
attrs = {href: "http://link", onclick: function(e) { }, custom: "value" };
for(key in attrs) a[key] = attrs[key];

--
Glenn Maynard
Tab Atkins Jr.
2011-08-03 15:50:43 UTC
Permalink
On Wed, Aug 3, 2011 at 8:46 AM, Glenn Maynard <***@zewt.org> wrote:
> On Wed, Aug 3, 2011 at 3:34 AM, Anne van Kesteren <***@opera.com> wrote:
>> On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
>> wrote:
>>> Hmm, is everything exposed as on* attributes now?  If so, then yeah,
>>> just do that; no need to mess around with a magic property in the
>>> attributes bag.
>>
>> This would still be magical as it is setting an IDL attribute rather than
>> a content attribute.
>
> What's the difference?  I'd expect this:
>
> a = Element.create("a", {href: "http://link", onclick: function(e) { },
> custom: "value" }, "link");
>
> to be essentially equivalent to
>
> a = document.createElement("a");
> a.appendChild(document.createTextNode("link"));
> attrs = {href: "http://link", onclick: function(e) { }, custom: "value" };
> for(key in attrs) a[key] = attrs[key];

Yes, because there you're setting the IDL attributes. Anne's assuming
that the intended virtual implementation instead has this as the final
line:

for(key in attrs) a.setAttribute(key,attrs[key]);

This would obviously produce a different result here, as the function
would be stringified into something useless.

For most attributes, there's no difference between the two approaches,
but a handful have significant differences.

~TJ
Glenn Maynard
2011-08-03 16:26:02 UTC
Permalink
On Wed, Aug 3, 2011 at 11:50 AM, Tab Atkins Jr. <***@gmail.com>wrote:

> for(key in attrs) a.setAttribute(key,attrs[key]);
>
> This would obviously produce a different result here, as the function
> would be stringified into something useless.
>

I think that means it's the wrong thing to do. Within JavaScript code, you
should be able to represent JS functions as JS functions, not as strings
containing JS functions.

The reasons are obvious, I think, but to spell out one use case:

function createWidget(target)
{
return Element.create("a", {
onclick: function(e) {
e.preventDefault();
target.activated();
}
}, "widget");
}

which is much harder to do if the function is a string.

On Wed, Aug 3, 2011 at 11:51 AM, Anne van Kesteren <***@opera.com> wrote:
> Would you expect to write contenteditable as contenteditable or as
contentEditable? Also, would you expect custom to end up as a content
attribute on that <link> element? Because it will not with this code.

I'd expect contentEditable, not contenteditable. I don't normally access
DOM object properties from JavaScript using their content attribute names.
I wouldn't expect a custom content attribute (there are cases where you want
to do that, but they're rare in my experience).

--
Glenn Maynard
Anne van Kesteren
2011-08-03 15:51:28 UTC
Permalink
On Wed, 03 Aug 2011 17:46:50 +0200, Glenn Maynard <***@zewt.org> wrote:
> What's the difference?

ele.setAttribute(x, val) works on any element. ele[x] = val does not. They
also behave differently for a large number of cases and the latter often
takes values of a type other than DOMString.


> I'd expect this:
>
> a = Element.create("a", {href: "http://link", onclick: function(e) { },
> custom: "value" }, "link");
>
> to be essentially equivalent to
>
> a = document.createElement("a");
> a.appendChild(document.createTextNode("link"));
> attrs = {href: "http://link", onclick: function(e) { }, custom: "value"
> };
> for(key in attrs) a[key] = attrs[key];

Would you expect to write contenteditable as contenteditable or as
contentEditable? Also, would you expect custom to end up as a content
attribute on that <link> element? Because it will not with this code.


--
Anne van Kesteren
http://annevankesteren.nl/
Charles Pritchard
2011-08-03 16:51:47 UTC
Permalink
On 8/3/2011 8:51 AM, Anne van Kesteren wrote:
> On Wed, 03 Aug 2011 17:46:50 +0200, Glenn Maynard <***@zewt.org> wrote:
>> What's the difference?
>
> ele.setAttribute(x, val) works on any element. ele[x] = val does not.
> They also behave differently for a large number of cases and the
> latter often takes values of a type other than DOMString.
Seems to me that aria- and data- prefixes are a special case:
data-* and aria-* should always run through the setAttribute code path.

IE9 does have aria attributes on the element as well, in camel case:
element.ariaSelected===element.getAttribute('aria-selected')

It's "always" going to be the case, that setting 'data-*' or 'aria-*'
will assign a DOM string content attribute.
>> I'd expect this:
>>
>> a = Element.create("a", {href: "http://link", onclick: function(e) { },
>> custom: "value" }, "link");
>>
>> to be essentially equivalent to
>>
>> a = document.createElement("a");
>> a.appendChild(document.createTextNode("link"));
>> attrs = {href: "http://link", onclick: function(e) { }, custom:
>> "value" };
>> for(key in attrs) a[key] = attrs[key];
>
> Would you expect to write contenteditable as contenteditable or as
> contentEditable? Also, would you expect custom to end up as a content
> attribute on that <link> element? Because it will not with this code.
>

contentEditable, tabIndex, innerHTML seem appropriate.
data-* and aria-* should always run through the setAttribute code path.

The DOM footprint should be light.

Example:
Element.create('a', { webkitShadow: Element.create('p','example shadow
paragraph') }, 'this is a link & button') .
attr({role: 'button', onclick: function() { alert('clicked!') }).
attr({tabIndex: 0, data-status: 'this is an example',
custom-attribute: 'not serialized'}).
attr({title: 'title example!', href: 'link.html'});

outerHTML, more or less:
<a title="title example!" role="button" tabindex="0" data-status="this
is an example" href="link.html">this is a link &amp; button</a>

webkitShadow here:
https://lists.webkit.org/pipermail/webkit-dev/2011-June/017340.html

If an author already has their string sanitized, they would pass through
the innerHTML attribute anyway.
var x = document.createElement; has not been a reliable shortcut.
var x = Element.create(); could be.

-Charles
Tab Atkins Jr.
2011-08-03 15:10:23 UTC
Permalink
On Wed, Aug 3, 2011 at 12:34 AM, Anne van Kesteren <***@opera.com> wrote:
> On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
> wrote:
>> On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
>>> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com>
>>> wrote:
>>>> MooTools is basically identical to Prototype, except that you can
>>>> additionally set listeners on the element during creation by using a
>>>> magical "events" property in the attribute bag, which takes an object
>>>> of event names and functions.  This would be nice to look into adding.
>>>
>>> Is this much better than just saying eg. Element.create("a", {href:
>>> "http://link", onclick: function(e) { ... } }, "link"}?
>>
>> Hmm, is everything exposed as on* attributes now?  If so, then yeah,
>> just do that; no need to mess around with a magic property in the
>> attributes bag.
>
> This would still be magical as it is setting an IDL attribute rather than a
> content attribute.

Hmm. onclick is a content attribute, no? Or do you just mean that
assigning a function directly (rather than a string of code) is
something that can only be done via an IDL attribute?

If so, then good point, but I also expect that this wouldn't be very confusing.

~TJ
Jonas Sicking
2011-08-04 21:51:52 UTC
Permalink
On Wed, Aug 3, 2011 at 8:10 AM, Tab Atkins Jr. <***@gmail.com> wrote:
> On Wed, Aug 3, 2011 at 12:34 AM, Anne van Kesteren <***@opera.com> wrote:
>> On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
>> wrote:
>>> On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
>>>> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com>
>>>> wrote:
>>>>> MooTools is basically identical to Prototype, except that you can
>>>>> additionally set listeners on the element during creation by using a
>>>>> magical "events" property in the attribute bag, which takes an object
>>>>> of event names and functions.  This would be nice to look into adding.
>>>>
>>>> Is this much better than just saying eg. Element.create("a", {href:
>>>> "http://link", onclick: function(e) { ... } }, "link"}?
>>>
>>> Hmm, is everything exposed as on* attributes now?  If so, then yeah,
>>> just do that; no need to mess around with a magic property in the
>>> attributes bag.
>>
>> This would still be magical as it is setting an IDL attribute rather than a
>> content attribute.
>
> Hmm.  onclick is a content attribute, no?  Or do you just mean that
> assigning a function directly (rather than a string of code) is
> something that can only be done via an IDL attribute?
>
> If so, then good point, but I also expect that this wouldn't be very confusing.

It would seem very inconsistent if some attributes are set using
elem.setAttribute and others using elem.foo=bar. Would you make the
distinction based on that the attribute name starts with "on"?

One possible solution would be to be able to specify event handler
attributes in a second object, so something like:

Element.create("a", { href: "..." }, { onclick: function(e) { ... } },
"link", anotherChild);

On the other hand, it might be ok to say that all attributes whose
name start with "on" and whose value is a Function object is set using
the IDL property rather than setAttribute.

/ Jonas
Garrett Smith
2011-08-05 05:45:41 UTC
Permalink
On 8/4/11, Jonas Sicking <***@sicking.cc> wrote:
> On Wed, Aug 3, 2011 at 8:10 AM, Tab Atkins Jr. <***@gmail.com> wrote:
>> On Wed, Aug 3, 2011 at 12:34 AM, Anne van Kesteren <***@opera.com>
>> wrote:
>>> On Tue, 02 Aug 2011 20:31:04 +0200, Tab Atkins Jr. <***@gmail.com>
>>> wrote:
>>>> On Tue, Aug 2, 2011 at 11:26 AM, Glenn Maynard <***@zewt.org> wrote:
>>>>> On Tue, Aug 2, 2011 at 2:18 PM, Tab Atkins Jr. <***@gmail.com>
>>>>> wrote:
>>>>>> MooTools is basically identical to Prototype, except that you can
>>>>>> additionally set listeners on the element during creation by using a
>>>>>> magical "events" property in the attribute bag, which takes an object
>>>>>> of event names and functions. This would be nice to look into adding.
>>>>>
>>>>> Is this much better than just saying eg. Element.create("a", {href:
>>>>> "http://link", onclick: function(e) { ... } }, "link"}?
>>>>
>>>> Hmm, is everything exposed as on* attributes now? If so, then yeah,
>>>> just do that; no need to mess around with a magic property in the
>>>> attributes bag.
>>>
>>> This would still be magical as it is setting an IDL attribute rather than
>>> a
>>> content attribute.
>>
>> Hmm. onclick is a content attribute, no? Or do you just mean that
>> assigning a function directly (rather than a string of code) is
>> something that can only be done via an IDL attribute?
>>
There is an event handler attribute named onclick. There is an event
handler property named onclick. Setting the property does not set the
attribute. Setting the attribute results in the creation of a function
with the value of that attribute as its FunctionBody and an augmented
scope chain. When the attribute is set, the property gets that
browser-generated function function.

The scope of handler attributes is explained in HTML 5, though incompletely:
http://dev.w3.org/html5/spec/Overview.html#event-handler-content-attributes
That's incomplete.

The scope chain of an event handler attribute for a form control would
function as so:

| <input type="text" onclick="alert([form, type]);" value="test">

function anonymous() {
with(document) {
with(this.form) {
with(this) {
alert([form, type]);
}
}
}
};

Or alternatively:
document.forms[0].elements[0].setAttribute("onclick", "alert(form, type])");

Result:
"[object HTMLFormElement],text"

Setting the property does not change the attribute. They're two
different things.

>> If so, then good point, but I also expect that this wouldn't be very
>> confusing.
>
I can see why you'd say that, but the fact is that attributes and
properties are different things.

If you take good care not to assign ID and NAME like "form" or "body"
(yeah, Google doesn't really heed that rule very well), you might not
see too many problems. And if you never ever use event handler
attributes, you won't get stung by scope augmentation bugs.

I wrote an article on this but Jim's server is perpetually down (as is
my site now). "Unsafe Names for Form Controls". Google cache:
http://webcache.googleusercontent.com/search?q=cache:VLOWitVtytgJ:www.jibbering.com/faq/names/event_handler.html+%22Event+Handler+Scope%22+jibbering&cd=1&hl=en&ct=clnk&gl=us&client=safari&source=www.google.com

See also:
http://code.google.com/p/chromium/issues/detail?id=80911&q=bzbarsky&colspec=ID%20Stars%20Pri%20Area%20Feature%20Type%20Status%20Summary%20Modified%20Owner%20Mstone%20OS

Though also explained many many years ago by Cornford on c.l.js.

> It would seem very inconsistent if some attributes are set using
> elem.setAttribute and others using elem.foo=bar.

Yeah, inconsistent. Sounds like jQuery.

Would you make the
> distinction based on that the attribute name starts with "on"?
>
I'm pretty sure Jonas would not do that.

> One possible solution would be to be able to specify event handler
> attributes in a second object, so something like:
>
> Element.create("a", { href: "..." }, { onclick: function(e) { ... } },
> "link", anotherChild);
>

The scope augmentation that goes along with the event handler
attribute is undesirable here.
--
Garrett
Garrett Smith
2011-08-05 06:22:19 UTC
Permalink
On 8/4/11, Garrett Smith <***@gmail.com> wrote:

[...]
ser-generated function function.
>
> The scope of handler attributes is explained in HTML 5, though
> incompletely:
> http://dev.w3.org/html5/spec/Overview.html#event-handler-content-attributes
> That's incomplete.
>
Correction: It is complete, I just misread it.
--
Garrett
Dominic Cooney
2011-08-06 16:05:43 UTC
Permalink
Element.create looks neat. Three thoughts:

First, I think Element.create *and* constructors like new
HTMLDivElement(attributes, children) are both useful. Element.create is good
when you have a tag name in hand, are creating unknown elements, or are
creating elements that don’t have a specific constructor (ins/del.)

New is good when you’re creating an element “statically.” It’s succinct, and
if you misspell the name you get an exception instead of an unknown element,
for example:

Element.create('vdieo', {src: '
'}) → no error, HTMLUnknownElement that
behaves nothing like <video>

new HTMLVdieoElement({src: '
'}) → ReferenceError, stack trace points me to
the faulty line of code.

When the element being created is “static” I think constructors are more
readable, too:

var b = Element.create('button', {}, [
Element.create('img', {src: 'http://
'}),
document.createTextNode('Click me!')
]);

compared to:

var b = new HTMLButtonElement({}, [
new Image('http://
'),
new TextNode('Click me!')
]);

Let me briefly reiterate that I think we want *both* Element.create and
constructors; they have complementary uses.

---

Second, re: setAttribute vs setting properties, there might be types other
than functions we want to not treat as strings; for example if a UA
implements CSSOM it might be nice to be able to set a style without having
to serialize and reparse the CSSStyleDeclaration.

Can we spec whether something in the attributes hash is set via setAttribute
or via setting a property based on the IDL for that element?

Alternatively we could provide a syntax, eg '@class': 'foo' to
setAttribute('class', 'foo') and className: 'foo' to set elem.className =
'foo'.

--

Third, is the order of attributes significant for XML namespace
declarations? eg does this:

<x xmlns:foo="
" foo:bar="
" />

mean the same thing as

<x foo:bar="
" xmlns:foo="
" />

? If not, including namespaces in the attribute dictionary is fraught,
because the iteration order of properties is undefined.

Dominic

On Fri, Aug 5, 2011 at 3:22 PM, Garrett Smith <***@gmail.com>wrote:

> On 8/4/11, Garrett Smith <***@gmail.com> wrote:
>
> [...]
> ser-generated function function.
> >
> > The scope of handler attributes is explained in HTML 5, though
> > incompletely:
> >
> http://dev.w3.org/html5/spec/Overview.html#event-handler-content-attributes
> > That's incomplete.
> >
> Correction: It is complete, I just misread it.
> --
> Garrett
>
>
Charles Pritchard
2011-08-06 19:11:54 UTC
Permalink
On 8/6/2011 9:05 AM, Dominic Cooney wrote:
> Element.create looks neat. Three thoughts:
...
> Let me briefly reiterate that I think we want *both* Element.create
> and constructors; they have complementary uses.
I agree.

>
> Second, re: setAttribute vs setting properties, there might be types
> other than functions we want to not treat as strings; for example if a
> UA implements CSSOM it might be nice to be able to set a style without
> having to serialize and reparse the CSSStyleDeclaration.
>
> Can we spec whether something in the attributes hash is set via
> setAttribute or via setting a property based on the IDL for that element?
How about .attributes as another reserved word:

Element.create('div', { attributes: { 'class': 'foo'} });
Element.create('div', { attributes: [ 'class','foo','class','bar'] }
).attr({textContent: 'Bar class'});

For reserved words: aria-*, data-* are always a DOMString via
setAttribute and
.attributes is an object or array used to run additional setAttribute calls.

If attributes were to accept a DOMString:
Element.create('div', { attributes: 'i-replaced="InnerHTML"
style="font-size: 2em"' });

...

Relevant to the discussion is Doug's createChild proposal:
http://www.w3.org/Graphics/SVG/WG/wiki/Simple_SVG_API#createChild

Doug adds an index integer as the third argument, something to consider.
Passing "0" as an argument is easier than firstChild().insertBefore()
semantics.

Example:
parentElement.createChildNS( htmlns, 'div', { data-test: 'test' }, 0);
// add to parent at position: 0.


Position could be a helpful abstraction:

Element.create('div').create(span,{textContent: 'second node'}).
position(0).create('span', {textContent: 'first node'});
<div><span>first node</span><span>second node</span>

Element.create('div').create('div').create('span', {textContent: 'span
node in two divs'}).
position(-1,0).create('span', {textContent: 'span node in first
div, first child.'});
<div><span>span node in first div, first child</span>
<div><span>span node in two divs</span></div></div>

Could be handy to use with querySelector.

-Charles
Garrett Smith
2011-08-06 22:54:34 UTC
Permalink
On 8/6/11, Charles Pritchard <***@jumis.com> wrote:
> On 8/6/2011 9:05 AM, Dominic Cooney wrote:
>> Element.create looks neat. Three thoughts:
> ...
>> Let me briefly reiterate that I think we want *both* Element.create
>> and constructors; they have complementary uses.
> I agree.

And for no reason, it seems.

>
>>
>> Second, re: setAttribute vs setting properties, there might be types
>> other than functions we want to not treat as strings; for example if a
>> UA implements CSSOM it might be nice to be able to set a style without
>> having to serialize and reparse the CSSStyleDeclaration.
>>
>> Can we spec whether something in the attributes hash is set via
>> setAttribute or via setting a property based on the IDL for that element?
> How about .attributes as another reserved word:
>
> Element.create('div', { attributes: { 'class': 'foo'} });
> Element.create('div', { attributes: [ 'class','foo','class','bar'] }
> ).attr({textContent: 'Bar class'});
>
textContent is a property, not an attribute. Properties vs attributes
was already explained in this thread.
[...]
--
Garrett
Charles Pritchard
2011-08-06 23:01:16 UTC
Permalink
On 8/6/2011 3:54 PM, Garrett Smith wrote:
> On 8/6/11, Charles Pritchard<***@jumis.com> wrote:
>> On 8/6/2011 9:05 AM, Dominic Cooney wrote:
>>> Element.create looks neat. Three thoughts:
>> ...
>>> Let me briefly reiterate that I think we want *both* Element.create
>>> and constructors; they have complementary uses.
>> I agree.
> And for no reason, it seems.
Dominic stated several reasons.
(Element.create('div') instanceof HTMLDivElement);


>>> Second, re: setAttribute vs setting properties, there might be types
>>> other than functions we want to not treat as strings; for example if a
>>> UA implements CSSOM it might be nice to be able to set a style without
>>> having to serialize and reparse the CSSStyleDeclaration.
>>>
>>> Can we spec whether something in the attributes hash is set via
>>> setAttribute or via setting a property based on the IDL for that element?
>> How about .attributes as another reserved word:
>>
>> Element.create('div', { attributes: { 'class': 'foo'} });
>> Element.create('div', { attributes: [ 'class','foo','class','bar'] }
>> ).attr({textContent: 'Bar class'});
>>
> textContent is a property, not an attribute. Properties vs attributes
> was already explained in this thread.
> [...]

I've stated in prior threads, that unless the word is aria-* or data-*,
properties are set on the element.
Here, I've added attributes as another reserved word, for setting
attributes.

The proposed attr() method sets properties, as create() does.

Element.create('div',{className: 'class'}).attr({className: 'class ', attributes: {'class': 'class'}).getAttribute('class') == 'class';
Garrett Smith
2011-08-08 09:03:44 UTC
Permalink
On 8/6/11, Charles Pritchard <***@jumis.com> wrote:
[...]
> I've stated in prior threads, that unless the word is aria-* or data-*,
> properties are set on the element.
I see. But that's inherently inconsistent and mostly misleading
("attr" alludes to "attribute" not "usually property except when it's
data or aria").

For setting properties, though, a `setOwnProperties` method could be
convenient.
--
Garrett
Tab Atkins Jr.
2011-08-08 07:52:32 UTC
Permalink
On Sat, Aug 6, 2011 at 9:05 AM, Dominic Cooney <***@google.com> wrote:
> Third, is the order of attributes significant for XML namespace
> declarations? eg does this:
> <x xmlns:foo="…" foo:bar="…" />
> mean the same thing as
> <x foo:bar="…" xmlns:foo="…" />
> ? If not, including namespaces in the attribute dictionary is fraught,
> because the iteration order of properties is undefined.

The order is unimportant when setting them via markup, but important
when setting them via successive setAttribute calls. I'd prefer that
the attribute bag be handled like markup attributes, where xmlns
attributes are handled "early" so that later attributes fall into the
correct namespace.

~TJ
Jonas Sicking
2011-08-08 08:17:45 UTC
Permalink
On Mon, Aug 8, 2011 at 12:52 AM, Tab Atkins Jr. <***@gmail.com> wrote:
> On Sat, Aug 6, 2011 at 9:05 AM, Dominic Cooney <***@google.com> wrote:
>> Third, is the order of attributes significant for XML namespace
>> declarations? eg does this:
>> <x xmlns:foo="…" foo:bar="…" />
>> mean the same thing as
>> <x foo:bar="…" xmlns:foo="…" />
>> ? If not, including namespaces in the attribute dictionary is fraught,
>> because the iteration order of properties is undefined.
>
> The order is unimportant when setting them via markup, but important
> when setting them via successive setAttribute calls.  I'd prefer that
> the attribute bag be handled like markup attributes, where xmlns
> attributes are handled "early" so that later attributes fall into the
> correct namespace.

Is there a reason to support namespaced attributes at all? They are
extremely rare, especially on the web.

Ideally I'd like to deprecate them, but I suspect that's not doable.
But I see no reason to support them in new APIs.

/ Jonas
Tab Atkins Jr.
2011-08-08 16:17:08 UTC
Permalink
On Mon, Aug 8, 2011 at 1:17 AM, Jonas Sicking <***@sicking.cc> wrote:
> Is there a reason to support namespaced attributes at all? They are
> extremely rare, especially on the web.
>
> Ideally I'd like to deprecate them, but I suspect that's not doable.
> But I see no reason to support them in new APIs.

SVG requires namespaced attributes for xlink, at least. We're
planning to get rid of that in SVG2, but for now it would be
necessary.

We could, of course, just say "Too bad, don't write things that need
the xlink namespace, and wait for SVG2 to get rid of them". I don't
think this would be very bad.

~TJ
Jonas Sicking
2011-08-08 16:59:05 UTC
Permalink
On Mon, Aug 8, 2011 at 9:17 AM, Tab Atkins Jr. <***@gmail.com> wrote:
> On Mon, Aug 8, 2011 at 1:17 AM, Jonas Sicking <***@sicking.cc> wrote:
>> Is there a reason to support namespaced attributes at all? They are
>> extremely rare, especially on the web.
>>
>> Ideally I'd like to deprecate them, but I suspect that's not doable.
>> But I see no reason to support them in new APIs.
>
> SVG requires namespaced attributes for xlink, at least.  We're
> planning to get rid of that in SVG2, but for now it would be
> necessary.
>
> We could, of course, just say "Too bad, don't write things that need
> the xlink namespace, and wait for SVG2 to get rid of them".  I don't
> think this would be very bad.

Yup, that's my take on it. It's easy enough for people to write two
lines of code instead of one for now.

/ Jonas
Julian Reschke
2011-08-08 09:17:36 UTC
Permalink
On 2011-08-08 10:17, Jonas Sicking wrote:
> On Mon, Aug 8, 2011 at 12:52 AM, Tab Atkins Jr.<***@gmail.com> wrote:
>> On Sat, Aug 6, 2011 at 9:05 AM, Dominic Cooney<***@google.com> wrote:
>>> Third, is the order of attributes significant for XML namespace
>>> declarations? eg does this:
>>> <x xmlns:foo="…" foo:bar="…" />
>>> mean the same thing as
>>> <x foo:bar="…" xmlns:foo="…" />
>>> ? If not, including namespaces in the attribute dictionary is fraught,
>>> because the iteration order of properties is undefined.
>>
>> The order is unimportant when setting them via markup, but important
>> when setting them via successive setAttribute calls. I'd prefer that
>> the attribute bag be handled like markup attributes, where xmlns
>> attributes are handled "early" so that later attributes fall into the
>> correct namespace.
>
> Is there a reason to support namespaced attributes at all? They are
> extremely rare, especially on the web.
>
> Ideally I'd like to deprecate them, but I suspect that's not doable.
> But I see no reason to support them in new APIs.

Isn't basic support cheap to get? Just allow the Clark notation
("{ns}local") for the attribute name.

Best regards, Julian
Jonas Sicking
2011-08-08 09:34:42 UTC
Permalink
On Mon, Aug 8, 2011 at 2:17 AM, Julian Reschke <***@gmx.de> wrote:
> On 2011-08-08 10:17, Jonas Sicking wrote:
>>
>> On Mon, Aug 8, 2011 at 12:52 AM, Tab Atkins Jr.<***@gmail.com>
>>  wrote:
>>>
>>> On Sat, Aug 6, 2011 at 9:05 AM, Dominic Cooney<***@google.com>
>>>  wrote:
>>>>
>>>> Third, is the order of attributes significant for XML namespace
>>>> declarations? eg does this:
>>>> <x xmlns:foo="…" foo:bar="…" />
>>>> mean the same thing as
>>>> <x foo:bar="…" xmlns:foo="…" />
>>>> ? If not, including namespaces in the attribute dictionary is fraught,
>>>> because the iteration order of properties is undefined.
>>>
>>> The order is unimportant when setting them via markup, but important
>>> when setting them via successive setAttribute calls.  I'd prefer that
>>> the attribute bag be handled like markup attributes, where xmlns
>>> attributes are handled "early" so that later attributes fall into the
>>> correct namespace.
>>
>> Is there a reason to support namespaced attributes at all? They are
>> extremely rare, especially on the web.
>>
>> Ideally I'd like to deprecate them, but I suspect that's not doable.
>> But I see no reason to support them in new APIs.
>
> Isn't basic support cheap to get? Just allow the Clark notation
> ("{ns}local") for the attribute name.

First off, that's infinitely more work to support a rarely used
feature than not supporting it at all.

Second, since that notation isn't used anywhere else, it's a pretty
big cost in brain print for users.

So no, I wouldn't say it's cheap.

/ Jonas
Julian Reschke
2011-08-08 10:02:26 UTC
Permalink
On 2011-08-08 11:34, Jonas Sicking wrote:
> ...
> First off, that's infinitely more work to support a rarely used
> feature than not supporting it at all.
>
> Second, since that notation isn't used anywhere else, it's a pretty
> big cost in brain print for users.
>
> So no, I wouldn't say it's cheap.
> ...

Well, it's cheap in that it needs no new API signature and uses a
notation that is already well-established.

Best regards, Julian
Garrett Smith
2011-08-02 18:27:32 UTC
Permalink
On 8/2/11, Tab Atkins Jr. <***@gmail.com> wrote:
> On Tue, Aug 2, 2011 at 9:48 AM, Aryeh Gregor <***@aryeh.name> wrote:
>> On Mon, Aug 1, 2011 at 9:33 PM, Maciej Stachowiak <***@apple.com> wrote:
>>> In an IRC discussion with Ian Hickson and Tab Atkins, we can up with the
>>> following idea for convenient element creation:
>>> Element.create(tagName, attributeMap, children…)
>>> Creates an element with the specified tag, attributes, and children.
>>
>> How does this compare to popular JS helper libraries like jQuery? It
Looking at it from a utilitarian perspective, I don't see why it matters.

>> would be useful to know what convenience APIs authors are using now
>> before introducing our own.
>
> jQuery's element creation is basically driven by innerHTML. That is,
> to create an element, you just make a call like "$('<p class=foo>')".
> I doubt that's a pattern we actually want to copy, as it's kinda
> dirty, and inconvenient in some cases. (For example, to apply a bag
> of properties as attributes,

Lost me on that one. Bag of properties? jQuery fumbled with confusing
attributes and properties for many years. No idea what they do now,
but probably similar.

you have to first create the element,
> then call attr() on it. You can't pass the attrs as an initial arg
> without string-building.)
>
> Prototype's element creation is almost identical to what is proposed
> here, except it uses something that looks like a constructor. You
> create an element with "new Element('p',{class:'foo'})". You can't
> set children as part of the initial call; they have to be appended in
> later calls.
>
And that is the reason that it's no good to go messing with this.
Prototype defines Element as a Function, there is the interface object
that is an Object.


> MooTools is basically identical to Prototype, except that you can
> additionally set listeners on the element during creation by using a
> magical "events" property in the attribute bag, which takes an object
> of event names and functions. This would be nice to look into adding.
>
Nope.
[...]
Big waste of time.
--
Garrett
Loading...