W3C home > Mailing lists > Public > public-script-coord@w3.org > April to June 2011

Re: indexed properties on NodeLists and HTMLCollections

From: Allen Wirfs-Brock <allen@wirfs-brock.com>
Date: Fri, 6 May 2011 09:42:57 -0700
Cc: Cameron McCormack <cam@mcc.id.au>, public-script-coord@w3.org
Message-Id: <EFA745F8-A8F1-4B12-A74B-3F28F45C90E3@wirfs-brock.com>
To: Boris Zbarsky <bzbarsky@MIT.EDU>
Let me take a shot a describe a semantics for the nodelist in your example.  I think what I describe is  consistent with the common behavior across current browsers but I may be wrong.  By this I mean that where browser now all do the same thing, this semantics also does that. Where there is new behavior it is either in areas where there are inconsistencies among browsers or where the behavior is making use of a new ES5 capabilities that are not currently used in the DOM.

On May 6, 2011, at 8:06 AM, Boris Zbarsky wrote:

> On 5/5/11 8:52 PM, Boris Zbarsky wrote:
>>> Well, the [OverrideBuiltins] behavior sounds like the normal native JS
>>> behavior.
>> 
>> Yes. However the normal native JS behavior is too restrictive for what's
>> needed here.
> 
> One other important note.  Consider this testcase, with line numbers:
> 
> 1:  var nodelist = document.getElementsByTagName("div");
> 2:  nodelist.myExpando = "1";
> 3:  alert(nodelist.myExpando);
> 4:  document.getElementById("somebox").innerHTML =
> 5:    someStringFromMyCMS;
> 6:  alert(nodelist.myExpando);
> 

Basic semantics for a such NamedNodeMaps (NodeList is the same but only with indexed own properties):

1) all own enumerable properties of the list object correspond to elements of the collection. (let's call such properties elements).  Elements may be either symbolically named or array indexed property.
2) normal ES prototype shadowing occurs.  In particular, elements shadow like-named properties defined on the prototype chain
3) element properties  manifest themselves as accessor properties.  [[Put]]/[[Get]]/[[Delete]]/[[DefineOwnProperty]] of elements have DOM side-effects. In practice, implementations would not normally reify these accessor properties.  Instead they would use implementation level mechanisms (Proxies if implemented in JS,  host object integration APIs in implemented in C++) to  trap [[Put]]/[[Get]]/etc. to implementer WebIDL getter/setter style access with the appropriate DOM side-effects.
4) the ES5 attributes of elements are configurable: true, enumerable: true. 
5) "type checking" is enforced on [[Put]] operations for elements. Setting an element to a invalid value  (a non-Node)  the list  throws
6) non-enumerable own properties of the list are not elements. They have no special semantics. If you want to add some local methods to a list make them non-enumerable:
    //make sure we can access the getNamedItem method
    Object.defineProperty(myList,"getNamedItem",{value:NamedNodeMap.prototype.getNamedItem, enumerable: false, configurable: false, writable: false});
7) A new element will not be created if doing so would redefine/over-write a non-configurable own property.
8) changing the attributes of an element via defineProperty to anything other than {configurable: true, enumerable: true} makes it a non-element.
9) possibly a few more loose ends but this is the basic idea...

Given these semantic rules we can see what the testcase would do:

step 2, creates/replaces an element named "myExpando"  (and also a corresponding array indexed element property).  this assumes that "1" is a valid element value for this list (it becomes a text node??).  If "1" is not a valid element value it does not create a property (element otherwise).  Instead it throws.

If in step 2 we wanted to created a non-element own property we would instead say:
2: Object.defineProperty(nodelist,"myExpando", {value:"1", enumerable: false, writable: true: configurable: true});

If we want this property to block the creation of like-named elements we would define it as:
2: Object.defineProperty(nodelist,"myExpando", {value:"1", enumerable: false, writable: true: configurable: false});

In either case, if "myExpando" already existed as an element, the element would first be removed from the list.

step 3 alerts the element created (or replaced) in step 2

step 6, alert undefined or an inherited property value if step 4 had a side effect of eliminating the "myExpando" element.  Otherwise return the current value of that element. I
Received on Friday, 6 May 2011 16:43:32 UTC

This archive was generated by hypermail 2.3.1 : Wednesday, 8 May 2013 19:30:03 UTC