This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 5851 - Consider adding .toArray() on NodeList and HTMLCollection
Summary: Consider adding .toArray() on NodeList and HTMLCollection
Status: RESOLVED WONTFIX
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: DOM (show other bugs)
Version: unspecified
Hardware: All All
: P5 enhancement
Target Milestone: ---
Assignee: Anne
QA Contact: public-webapps-bugzilla
URL: https://www.w3.org/Bugs/Public/show_b...
Whiteboard:
Keywords: NoReply
Depends on:
Blocks:
 
Reported: 2008-07-10 06:53 UTC by Ian 'Hixie' Hickson
Modified: 2013-04-04 19:02 UTC (History)
12 users (show)

See Also:


Attachments

Description Ian 'Hixie' Hickson 2008-07-10 06:53:33 UTC
From http://alex.dojotoolkit.org/?p=623 :

Fast LiveCollection -> Array Transforms: That many DOM apis return live collections is a bug, but it need not be fatal. Browser vendors could start to provide a simple toArray() method on these live collections to provide a way to “fix” them in place.
Comment 1 crisp 2008-07-10 23:04:05 UTC
'live collections' are not a bug; they are a design feature and you could use that to your advantage. They are on the other side often misunderstood or not anticipated which could lead to bugs or unexpected results.

On the other hand it is already quite easy to convert such collections to an array in a browser that has implemented Array in a generic way so that it also works on interface objects:

var foo = document.getElementsByTagName('div');
var staticFoo = [].slice.call(foo, 0);

the toArray() could then easily be implemented by prototyping NodeList or HTMLCollection (don't know which one or which browsers actually support that)

I'd rather see a specification that says that Array methods should be generic and thus also work on interface objects (that can be accessed like an array having a length property and all) and that interface objects should be first-class javascript objects and thus be expandable.
Comment 2 crisp 2008-07-11 00:21:14 UTC
In addition: the performance element brought by alex is just a straw hat; most of the time core JS and DOM operations are just a fraction of dynamic HTML updates. (partial) Document re-rendering is mostly more time consuming.
Comment 3 Lachlan Hunt 2008-07-11 08:03:42 UTC
(In reply to comment #1)
> 'live collections' are not a bug; they are a design feature and you could use
> that to your advantage.

Even though it's by design, some people consider it a bug because it's very unintuitive.

> On the other hand it is already quite easy to convert such collections to an
> array in a browser that has implemented Array in a generic way so that it also
> works on interface objects:
> 
> var foo = document.getElementsByTagName('div');
> var staticFoo = [].slice.call(foo, 0);


> the toArray() could then easily be implemented by prototyping NodeList or
> HTMLCollection (don't know which one or which browsers actually support that)

It's good to know that it can be so easily provided like that for backwards compatibility, but it's likely that a native implementation would be a bit more efficient.

> I'd rather see a specification that says that Array methods should be generic
> and thus also work on interface objects (that can be accessed like an array
> having a length property and all) and that interface objects should be
> first-class javascript objects and thus be expandable.

This can't work because NodeLists are live (except in Selectors API).  If they weren't live, it would have been possible to consider something like that.

Consider what it would mean for some of the mutator methods of Array to be used directly on a NodeList. e.g.

var foo = document.getElementsByTagName('div');
foo.reverse();
foo.pop();
...

The toArray() method solves this problem, since one can easily do:

var foo = document.getElementsByTagName('div').toArray();

and not have to deal with the NodeList any more.
Comment 4 crisp 2008-07-11 08:20:50 UTC
If efficiency is key than it makes more sense to add an extra argument to the method that says it's to return a static array iso a live nodelist. That way you're even allowed to do this:

var foo = document.getElementsByTagName('div', RETURN_STATIC_ARRAY).reverse();

:)
Comment 5 Lachlan Hunt 2008-07-11 08:59:21 UTC
(In reply to comment #4)
> If efficiency is key than it makes more sense to add an extra argument to the
> method that says it's to return a static array iso a live nodelist. That way
> you're even allowed to do this:
> 
> var foo = document.getElementsByTagName('div', RETURN_STATIC_ARRAY).reverse();

That's not backwards compatible and makes it more difficult for JS libraries to provide the functionality in legacy browsers.  It would require the script to do replace the method itself, check for the extra paramter and return an array.

document._getElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function(tagName, static) {
  var list = document._getElementsByTagName(tagName);
  if (static) {
    var array = [].slice.call(foo, 0);
    return array;
  }
  return list;
}

That would also have to be done separately for every other existing and future method that returns a NodeList on both document and the element nodes.  Adding a single method that deals with all node lists regardless of where they come from is much easier to implement and use.
Comment 6 crisp 2008-07-11 09:49:24 UTC
The same goes for toArray(), you do need to implement switching logic anyway because this simply will not work either:

var foo = document.getElementsByTagName('div').toArray();

and afaik there is no way in legacy browsers to make this work since one browser doesn't support expando's on interface objects *at all* and I can't even make this work in at least Firefox:

if (!HTMLCollection.prototype.toArray)
{
 HTMLCollection.prototype.toArray = function()
 {
  return [].slice.call(this, 0);
 }
}

(assuming this is the right object, I tried NodeList too for good measure)

the only option is to prototype toArray on Object, and we all know that that's not good practice.

Imo a generic toArray method for live nodelists is just syntax sugering and doesn't really solve the problem of the concept of live nodelists being unintuitive; you will need to know about the fact anyhow to know that you can use toArray() (in some unforeseeable future).

And like I said: if performance is key a native toArray method is far less performant than an extra argument. And how much will it's performance gain be over [].slice.call? Will that be worth the trouble?
Comment 7 Ian 'Hixie' Hickson 2009-05-24 01:07:51 UTC
In the interests of not getting ahead of the implementations, I'm going to punt on this for now. I think it makes sense to do something like this (I've even suggested the same myself years ago).
Comment 8 Maciej Stachowiak 2010-03-14 13:15:11 UTC
This bug predates the HTML Working Group Decision Policy.

If you are satisfied with the resolution of this bug, please change the state of this bug to CLOSED. If
you have additional information and would like the editor to reconsider, please reopen this bug. If you would like to escalate the issue to the full HTML Working Group, please add the TrackerRequest keyword to this bug, and suggest title and text for the tracker issue; or you may create a tracker issue yourself, if you are able to do so. For more details, see this document:
   http://dev.w3.org/html5/decision-policy/decision-policy.html

This bug is now being moved to VERIFIED. Please respond within two weeks. If this bug is not closed, reopened or escalated within two weeks, it may be marked as NoReply and will no longer be considered a pending comment.
Comment 9 public-rdfa-wg 2013-01-24 06:53:52 UTC
This bug was cloned to create HTML WG bug 19045.
Comment 10 Ian 'Hixie' Hickson 2013-03-18 22:31:59 UTC
Relevant features are now in DOM.
Comment 11 Anne 2013-03-31 12:52:11 UTC
arv, ECMAScript 6 is solving this right? Or do we need to add something?
Comment 12 Erik Arvidsson 2013-04-04 18:56:54 UTC
NodeLists should implement the iteration protocol of ES6 so one can do

  for (let node of nodelist) { ... }

as well as,

  let array = [...nodelist];

but also an API call,

  let array = Array.from(nodelist);

These should be enough to not want to add a toArray method at this point.
Comment 13 Boris Zbarsky 2013-04-04 19:02:15 UTC
>  for (let node of nodelist) { ... }

This one already works at least in Gecko+SpiderMonkey, fwiw.