NodeList

Summary

NodeList objects are collections of nodes such as those returned by Node.childNodes and the querySelectorAll method.

Properties

length

The number of nodes in the NodeList.

Methods

item ( idx )
Returns an item in the list by its index, or null if out-of-bounds. Equivalent to nodeList[idx].

Description

A "live" collection

In some cases, the NodeList is a live collection. This means that changes in the DOM are reflected in the collection.

var divs = document.getElementsByTagName( 'div' );

var firstDiv = divs[ 0 ];
// firstDiv.childNodes.length === 2 for instance.

firstDiv.appendChild( document.createElement( 'div' ) ); // another div is added as child
// the firstDiv.childNodes NodeList is automatically updated
// firstDiv.childNodes.length === 3 now.

If the NodeList is the return value of document.querySelectorAll, it is NOT live.

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, however they don't have those methods.

JavaScript has an inheritance mechanism based on prototypes for both built–in objects (like Arrays) and host objects (like NodeLists). 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 Object.getPrototypeOf several times.)

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.

Workarounds

One idea would be to add Array.prototype methods to NodeList.prototype. However, be aware that Extending the DOM is dangerous, especially in old version of Internet Explorer (6, 7, 8).

var arrayMethods = Object.getOwnPropertyNames( Array.prototype );

arrayMethods.forEach( attachArrayMethodsToNodeList );

function attachArrayMethodsToNodeList(methodName)
{
    NodeList.prototype[methodName] = Array.prototype[methodName];
};
 
var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

firstDiv.childNodes.forEach(function( divChild ){
  divChild.parentNode.style.color = '#0F0';
});

Another approach without extending the DOM:

var forEach = Array.prototype.forEach;

var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

forEach.call(firstDiv.childNodes, function( divChild ){
  divChild.parentNode.style.color = '#0F0';
});

Note that in the above, passing a host object (like a NodeList) as this to a native method (such as forEach) is not guaranteed to work in all browsers and is known to fail in some.

Example

It's possible to loop over the items in a NodeList using:

for (var i = 0; i < myNodeList.length; ++i) {
  var item = myNodeList[i];  // Calling myNodeList.item(i) isn't necessary in JavaScript
}

Don't be tempted to use for...in or for each...in to enumerate the items in the list, since that will also enumerate the length and item properties of the NodeList and cause errors if your script assumes it only has to deal with element objects. Also, for..in is not guaranteed to visit the properties in any particular order.

for...of loops will loop over NodeList objects correctly, in browsers that support for...of (like Firefox 13 and later):

var list = document.querySelectorAll( 'input[type=checkbox]' );
for (var item of list) {
  item.checked = true;
}

 

Specification