Revision 400511 of NodeList

  • Revision slug: Web/API/NodeList
  • Revision title: NodeList
  • Revision id: 400511
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment mention for...of loopsDOM/NodeList Web/API/NodeList

Revision Content

Summary

NodeList objects are collections of nodes returned by getElementsByTagName, getElementsByTagNameNS, {{domxref("Node.childNodes")}}, querySelectorAll, getElementsByClassName, etc.

Properties

length

Reflects the number of elements 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 most cases, the NodeList is a live collection. This means that changes on the DOM tree are going to be reflected on the collection.

var links = document.getElementsByTagName('a');
// links.length === 2 for instance.

document.body.appendChild( links[0].cloneNode(true) ); // another link is added to the document
// the 'links' NodeList is automatically updated
// links.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. 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.

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).

//The code can not executed properly for some reason
//1. The Array.prototype's property are not enumerable,so using for...in loop cannot get the property
//2. NodeList[prop] = Array.prototype[prop] this way,the property is added to the NodeList function, but not the instance
//   so we need to add properties to NodeList.prototype

var _arrayProto = Array.prototype;
var _funcList = Object.getOwnPropertyNames.call(null,Array.prototype);
var _element;
var _theFunc;

for(var prop in _funcList){
  _element = _funcList[prop];
  _theFunc = _arrayProto[_element];
  if(_arrayProto.hasOwnProperty(_element) && typeof(_theFunc) === 'function'){
    NodeList.prototype[_element] = _theFunc;
    console.log(_theFunc.name + ' has been added!');
  }
}

var links = document.getElementsByTagName('a');
links.forEach(function(link){
  link.style.color = '#0F0';
});

 

Another approach without extending the DOM:

var forEach = Array.prototype.forEach;

var links = document.getElementsByTagName('a');
forEach.call(links, function(link){ // works in most browsers (please document here if you find browsers where it doesn't)
  link.style.color = '#0F0';
});

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 {{domxref("element")}} objects.

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

Revision Source

<h2 id="Summary">Summary</h2>
<p>NodeList objects are collections of nodes returned by <a href="/en-US/docs/DOM/document.getElementsByTagName" title="document.getElementsByTagName"><code>getElementsByTagName</code></a>, <a href="/en-US/docs/DOM/document.getElementsByTagNameNS" title="document.getElementsByTagNameNS"><code>getElementsByTagNameNS</code></a>, {{domxref("Node.childNodes")}}, <a href="/en-US/docs/DOM/Document.querySelectorAll" title="document.querySelectorAll">querySelectorAll</a>, <a href="/en-US/docs/DOM/document.getElementsByClassName" title="document.getElementsByClassName"><code>getElementsByClassName</code></a>, etc.</p>
<h2 id="Properties">Properties</h2>
<p><code><span style="font-weight: bold;">length</span></code></p>
<dl>
  <dd>
    Reflects the number of elements in the NodeList.</dd>
</dl>
<h2 id="Methods">Methods</h2>
<dl>
  <dt>
    <code><a href="/en-US/docs/DOM/NodeList.item" title="DOM/NodeList.item">item</a> ( idx )</code></dt>
</dl>
<dl>
  <dd>
    Returns an item in the list by its index, or <code>null</code> if out-of-bounds. Equivalent to nodeList[idx]</dd>
</dl>
<h2 id="Description">Description</h2>
<h3 id="A_.22live.22_collection">A "live" collection</h3>
<p>In most cases, the NodeList is a live collection. This means that changes on the DOM tree are going to be reflected on the collection.</p>
<pre class="brush: js">
var links = document.getElementsByTagName('a');
// links.length === 2 for instance.

document.body.appendChild( links[0].cloneNode(true) ); // another link is added to the document
// the 'links' NodeList is automatically updated
// links.length === 3 now.
</pre>
<p>If the NodeList is the return value of document.querySelectorAll, it is <strong>NOT</strong> live.</p>
<h3 id="Why_can't_I_use_forEach_or_map_on_a_NodeList.3F">Why can't I use forEach or map on a NodeList?</h3>
<p>NodeList are used very much like arrays and it would be tempting to use Array.prototype methods on them. This is, however, impossible.</p>
<p>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:</p>
<p><code>myArray --&gt; Array.prototype --&gt; Object.prototype --&gt; null</code> (the prototype chain of an object can be obtained by calling several times Object.getPrototypeOf)</p>
<p>forEach, map and the likes are own properties of the Array.prototype object.</p>
<p>Unlike arrays, NodeList prototype chain looks like the following:</p>
<p><code>myNodeList --&gt; NodeList.prototype --&gt; Object.prototype --&gt; null</code></p>
<p><code>NodeList.prototype</code> contains the <code>item</code> method, but none of the Array.prototype methods, so they cannot be used on NodeLists.</p>
<h4 id="Workarounds">Workarounds</h4>
<p>One idea would be to add Array.prototype methods to NodeList.prototype. However, be aware that <a href="http://perfectionkills.com/whats-wrong-with-extending-the-dom/">Extending the DOM is dangerous, especially in old version of Internet Explorer (6, 7, 8)</a>.</p>
<pre class="brush: js">
//The code can not executed properly for some reason
//1. The Array.prototype's property are not enumerable,so using for...in loop cannot get the property
//2. NodeList[prop] = Array.prototype[prop] this way,the property is added to the NodeList function, but not the instance
//   so we need to add properties to NodeList.prototype

var _arrayProto = Array.prototype;
var _funcList = Object.getOwnPropertyNames.call(null,Array.prototype);
var _element;
var _theFunc;

for(var prop in _funcList){
  _element = _funcList[prop];
  _theFunc = _arrayProto[_element];
  if(_arrayProto.hasOwnProperty(_element) &amp;&amp; typeof(_theFunc) === 'function'){
    NodeList.prototype[_element] = _theFunc;
    console.log(_theFunc.name + ' has been added!');
  }
}

var links = document.getElementsByTagName('a');
links.forEach(function(link){
  link.style.color = '#0F0';
});</pre>
<p>&nbsp;</p>
<p>Another approach without extending the DOM:</p>
<pre class="brush: js">
var forEach = Array.prototype.forEach;

var links = document.getElementsByTagName('a');
forEach.call(links, function(link){ // works in most browsers (please document here if you find browsers where it doesn't)
  link.style.color = '#0F0';
});
</pre>
<h2 id="Example">Example</h2>
<p>It's possible to loop over the items in a <code>NodeList</code> using:</p>
<pre class="brush: js">
for (var i = 0; i &lt; myNodeList.length; ++i) {
  var item = myNodeList[i];  // Calling myNodeList.item(i) isn't necessary in JavaScript
}
</pre>
<p>Don't be tempted to use <code><a href="/en-US/docs/JavaScript/Reference/Statements/for...in" title="JavaScript/ Reference/Statements/for...in">for...in</a></code> or <code><a href="/en-US/docs/JavaScript/Reference/Statements/for_each...in" title="JavaScript/ Reference/Statements/for each...in">for each...in</a></code> to enumerate the items in the list, since that will also enumerate the length and item properties of the <code>NodeList</code> and cause errors if your script assumes it only has to deal with {{domxref("element")}} objects.</p>
<p><code><a href="/en-US/docs/JavaScript/Reference/Statements/for...of" title="/en-US/docs/JavaScript/Reference/Statements/for...of">for...of</a></code> loops will loop over NodeList objects correctly, in browsers that support <code>for...of </code>(like Firefox 13 and later):</p>
<pre>
var list = document.querySelectorAll("input[type=checkbox]");
for (var item of list) {
  item.checked = true;
}</pre>
<p>&nbsp;</p>
<h2 id="Specification">Specification</h2>
<ul>
  <li><a href="http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-536297177" title="http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-536297177">DOM Level 3</a></li>
</ul>
Revert to this revision