[whatwg] Adding ECMAScript 5 array extras to HTMLCollection

J Z kangax.dev at gmail.com
Sun Apr 25 00:39:39 PDT 2010

On Sun, Apr 25, 2010 at 2:33 AM, David Bruant <bruant at enseirb-matmeca.fr>wrote:

>  Le 24/04/2010 22:50, J Z a écrit :
> On Fri, Apr 23, 2010 at 10:30 PM, David Bruant <bruant at enseirb-matmeca.fr>wrote:
>> Hi,
>> In the HTML5 "status of this document" section, one can read : "This
>> specification is intended to replace (be the new version of) what was
>> previously the [...] DOM2 HTML specifications."
>> This spec can be found here : http://www.w3.org/TR/DOM-Level-2-HTML/
>> It defines ECMAScript language Binding (
>> http://www.w3.org/TR/DOM-Level-2-HTML/ecma-script-binding.html). This
>> document explains how to implement the DOM HTML interfaces in an
>> ECMAScript-compliant environment.
>> Because HTML5 is intended to replace DOM2 HTML, it can "freely" change
>> ECMAScript bindings. My suggestion is the following :
>> Make that HTMLCollection (and all HTML*Collection, as a consequence of
>> inheritence of HTMLCollection) inherit from the ECMAScript Array prototype.
>> This way, it will make available all Array extra methods (forEach, map,
>> filter...) added in ECMAScript5 (and next versions which should go in the
>> same direction).
>> As far as I know, adding this won't break any existing code. The semantics
>> of a Collection and the way it is used is very close from ECMAScript Arrays.
>> I don't think that the notion of "live object" and ECMAScript Array are
>> incompatible either.
>> Once again, I am talking about ECMAScript binding. I have no intention to
>> touch the HTMLCollection interface or other languages bindings.
>> Would the WHATWG have the power to decide something similar regarding
>> NodeList ?
>> Any thoughts ?
>> Thanks,
>> David
> As far as I can see, liveness of HTMLCollection actually does matter. When
> iterating over HTMLCollection, it's more or less a rule of thumb to "save"
> length, to avoid any kind of mismatch (in case code within loop modifies
> document and so affects length of collection in question):
> for (var i = 0, length = collection.length; i < length; i++)
> // instead of:
> for (var i = 0; i < collection.length; i++)
> I think I can take your point as a "pro" more than a "con", because in ES5,
> right before the definition of each array extra method, a paragraph like the
> following can be found :
> "The range of elements processed by forEach is set before the first call to
> callbackfn. Elements which are appended to the array after the call to
> forEach begins will not be visited by callbackfn. If existing elements of
> the array are changed, their value as passed to callback will be the value
> at the time forEach visits them; elements that are deleted after the call to
> forEach begins and before being visited are not visited."
> This point is confirmed by every algorithm where the length is "saved" once
> for all before the loop and not got from the .length property each time.

Oh, perfect :)

If HTMLCollection was inheriting from Array, and methods like `forEach`,
`map`, etc. were to operate on a live object, there would definitely be
undesired consequences. We can see this in, say, Firefox (which allows to
set [[Prototype]] of `HTMLCollection` to `Array.prototype`):

HTMLCollection.prototype.__proto__ = Array.prototype;

document.getElementsByTagName('div').forEach(function(el) {
  el.parentNode.removeChild(el); // doesn't work as expected

This code doesn't work as expected as the following doesn't either :
> var divs = document.getElementsByTagName('div');
> for(var i=0, l = divs.length ; i < l ; i++){
>     var el = divs[i]; // Due to the live-ness, this might not work as
> expected
>     el.parentNode.removeChild(el);
> }
> This code written as a for-loop behave exactly the same way (in this case)
> as the .forEach one, so it's as buggy as the forEach one.

Sorry, that was a stupid example indeed. It should have been at least
something along the lines of:

var els = document.getElementsByTagName('span');

for (var i = 0; i < els.length; /* can't access length dynamically */ i++) {
  var spanEl = document.createElement('span');

> My point is that forEach doesn't create more bugs than before, which is
> what you seem to imply.

If it operates on "static" collection, then I don't see problems either.

> Adding .forEach and other Array extras wouldn't prevent programmers to
> remember that they are dealing with a live object even within a .forEach,
> the same way they are not supposed to forget it with a for-loop.


> I have thought a lot about weirdnesses that people could think about like
> trying to assign a value to the HTMLCollection (divs[14] = myOtherDiv), but
> once again, it wouldn't be more allowed than it currently is (I have no idea
> of what happens today, but if an error is thrown in a for-loop, it should
> throw an error as well in a call within a forEach).

How would destructive methods like `push` or `sort` behave? Would
`document.body.childNodes.push(document.createTextNode('foo'))` append text
node to a body element? Or would it be a noop?

> // turning live collection into static array fixes this
> Array.slice(document.getElementsByTagName('div')).forEach(function(el) {
>   el.parentNode.removeChild(el);
> });
> I have not found anything about a Array.slice method and the ES5 .splice
> method doesn't seem to by usable in the way you describe. What did you mean
> ?

That's array generics in Mozilla (
I used it for brevity, but it could have been standard
`Array.prototype.slice.call(collection, 0)` instead (in environments which
allow such conversion of course).


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/attachments/20100425/db7f0b76/attachment-0002.htm>

More information about the whatwg mailing list