Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't nodelist have forEach?

I was working on a short script to change <abbr> elements' inner text, but found that nodelist does not have a forEach method. I know that nodelist doesn't inherit from Array, but doesn't it seem like forEach would be a useful method to have? Is there a particular implementation issue I am not aware of that prevents adding forEach to nodelist?

Note: I am aware that Dojo and jQuery both have forEach in some form for their nodelists. I cannot use either due to limitations.

like image 779
Snakes and Coffee Avatar asked Nov 17 '12 19:11

Snakes and Coffee


People also ask

Can you do forEach on a NodeList?

forEach() The forEach() method of the NodeList interface calls the callback given in parameter once for each value pair in the list, in insertion order.

How do I iterate through NodeList?

Note: Although NodeList is not an Array , it is possible to iterate over it with forEach() . It can also be converted to a real Array using Array. from() .

Can you use array methods on NodeList?

A NodeList is an array-like object that represents a collection of DOM elements or more specifically nodes. It is just like an array, but you can not use the common array methods like map() , slice() , and filter() on a NodeList object.

What is the difference between NodeList and HTMLCollection?

The Difference Between an HTMLCollection and a NodeList The index starts at 0. Both have a length property that returns the number of elements in the list (collection). An HTMLCollection is a collection of document elements. A NodeList is a collection of document nodes (element nodes, attribute nodes, and text nodes).


5 Answers

NodeList now has forEach() in all major browsers

See nodeList forEach() on MDN.

Original answer

None of these answers explain why NodeList doesn't inherit from Array, thus allowing it to have forEach and all the rest.

The answer is found on this es-discuss thread. In short, it breaks the web:

The problem was code that incorrectly assumed instanceof to mean that the instance was an Array in combination with Array.prototype.concat.

There was a bug in Google's Closure Library which caused almost all Google's apps to fail due to this. The library was updated as soon as this was found but there might still be code out there that makes the same incorrect assumption in combination with concat.

That is, some code did something like

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

However, concat will treat "real" arrays (not instanceof Array) differently from other objects:

[1, 2, 3].concat([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat(4) // [1, 2, 3, 4]

so that means that the above code broke when x was a NodeList, because before it went down the doSomethingElseWith(x) path, whereas afterward it went down the otherArray.concat(x) path, which did something weird since x wasn't a real array.

For some time there was a proposal for an Elements class that was a real subclass of Array, and would be used as "the new NodeList". However, that was removed from the DOM Standard, at least for now, since it wasn't feasible to implement yet for a variety of technical and specification-related reasons.

like image 125
Domenic Avatar answered Oct 17 '22 07:10

Domenic


You can do

Array.prototype.forEach.call (nodeList, function (node) {

    // Your code here.

} );
like image 66
akuhn Avatar answered Oct 17 '22 08:10

akuhn


You can consider creating a new array of nodes.

  var nodeList = document.getElementsByTagName('div'),

      nodes = Array.prototype.slice.call(nodeList,0); 

  // nodes is an array now.
  nodes.forEach(function(node){ 

       // do your stuff here.  

  });

Note: This is just a list/array of node references we are creating here, no duplicate nodes.

  nodes[0] === nodeList[0] // will be true
like image 33
sbr Avatar answered Oct 17 '22 06:10

sbr


In short, its a design conflict to implement that method.

From MDN:

Why can't I use forEach or map on a NodeList?

NodeList are used very much like arrays and it would be tempting to use Array.prototype methods on them. This is, however, impossible.

JavaScript has an inheritance mechanism based on prototypes. Array instances inherit array methods (such as forEach or map) because their prototype chain looks like the following:

myArray --> Array.prototype --> Object.prototype --> null (the prototype chain of an object can be obtained by calling several times Object.getPrototypeOf)

forEach, map and the likes are own properties of the Array.prototype object.

Unlike arrays, NodeList prototype chain looks like the following:

myNodeList --> NodeList.prototype --> Object.prototype --> null

NodeList.prototype contains the item method, but none of the Array.prototype methods, so they cannot be used on NodeLists.

Source: https://developer.mozilla.org/en-US/docs/DOM/NodeList (scroll down to Why can't I use forEach or map on a NodeList?)

like image 20
Matt Lo Avatar answered Oct 17 '22 06:10

Matt Lo


Never say never, it's 2016 and the NodeList object has implemented a forEach method in latest chrome (v52.0.2743.116).

It's too early to use it in production as other browser don't support this yet (tested FF 49) but I would guess that this will be standardized soon.

like image 20
maioman Avatar answered Oct 17 '22 06:10

maioman