There are many ways to iterate a NodeList.
In general, it is not a good idea to turn a nodeList into an Array or to use an Array's functions in reference to a nodeList.
You can use a good old fashioned for loop, start at zero and loop until we reach the end of the array. This method has been around for ever and is still used regularly today. This method is, somewhat, less readable than other methods mentioned here, but it all comes down to what is easier for you to write.
for(var i = 0; i < nodeList.length; ++i) doSomething(nodeList[i]);
Some people will tell you that using a backwards for loop will save "computation cycles", whatever that really means. In fact, some IDEs will actually convert the previous loop to the following structure by default.
In reality, micro-optimization is frowned upon. There is no real tangible difference in performance between this method and the other methods mentioned here.
for(var i = nodeList.length - 1; i > -1; --i) doSomething(nodeList[i]);
You can use a while loop, which expects a conditional statement as its parameter. If NodeList.item(n)
is past the bounds of the NodeList it will return null, which will end the loop.
var i = 0;
while((node = nodeList.item(i++))) doSomething(node);
Another method of using a for loop, which is similar to the while loop. The middle condition of a traditional for loop expects a conditional statement, just like the while loop above, so this works.
for(var i = 0; (node = nodeList.item(i)); i++) doSomething(node);
You can use a for...in loop with Object.keys()
. Note that you have to use Object.keys
when using a for...in loop because otherwise it will iterate over the non-enumerable properties as well as the enumerable ones.
The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
for(var i in Object.keys(nodeList)) doSomething(nodeList[i]);
In ES6, you can use a for...of loop by retrieving the Iterator function from Array() and applying it to the NodeList. This will work for most other uses of an object as well, as long as the properties are enumerable.
nodeList[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);
Also, cool enough, the following works in ES6. What you're doing here is retrieving the Symbol.iterator function from the Array and applying that to the NodeList object's prototype. This way, whenever a new NodeList is created, it will always have the iterator, so you only have to do that at the beginning of the script.
(function(){
"use strict";
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
for(let node of document.querySelectorAll('*')) document.body.innerHTML += node.tagName + ' ';
document.body.innerHTML += '<br>';
for(let node of document.querySelectorAll('*:not(body)')) document.body.innerHTML += node.tagName + ' ';
})();
There are variations on each of the previously mentioned methods as well as there are probably other methods that I haven't documented here, but the methods documented above give you the general idea of how you can iterate a NodeList without tricking array functions.
I want to iterate a NodeList using forEach method
Why? That is the real issue here. While you can iterate a NodeList by tricking an array function into believing that a NodeList is an Array, why would you want to?
I googled about the method, and I found the solution to do that is to convert the NodeList to an Array.
Yes, that is one method of hijacking an Array function in order to iterate a NodeList.
But I don't understand why we used the slice method?
Array.prototype.slice()
returns a shallow copy of elements from the original array. Using .call()
allows us to supplement the value of this
to our NodeList for slice()
.
Array.prototype.slice.call(NodeList)
returns an array representing a shallow copy of the object's enumerable properties.
There are numerous methods of hijacking Array functions and tricking them into thinking that they are working with an Array while feeding them an Object.
I personally believe that any of the following methods are horrible code but, because you asked about it, I will include any relevant array functions that can be used on a NodeList.
Array.prototype.slice.call(nodeList).forEach(doSomething);
[].slice.call(nodeList).forEach(doSomething);
You don't need to make an array out of the NodeList, you can simply use .call()
on the forEach
function directly.
Array.prototype.forEach.call(nodeList, doSomething);
[].forEach.call(nodeList, doSomething);
Or you can use map to create a new array from the nodeList based on a formula, then iterate the NodeList using forEach.
Array.prototype.map.call(nodeList, function(item) {
return item;
}).forEach(doSomething);
[].map.call(nodeList, function(item) {
return item;
}).forEach(doSomething);
These work because an Array is a type of Object, and a NodeList is similar enough to an Array that you can use some of the Array functions with a NodeList.
However, these methods should not be encouraged, there are much better ways to iterate a NodeList
Finally, if you don't want to hijack any array functions, but do want to use forEach, you can build an array from the NodeList.
var array = new Array(els.length);
for(var key in Object.keys(nodeList)) arrary[key] = nodeList[key];
array.forEach(doSomething);
This is somewhat redundant because you are basically iterating the NodeList twice.
The important thing to note here, is that there is no real tangible difference between any of these methods as far as performance is concerned. All of these methods will perform at a relatively similar rate and if you get to a point where you are iterating over enough elements that it would actually matter, you should ask yourself if there is better approach that you should be taking to solve your problem.
As for which method is better than the rest, it all depends on what you feel is more easily understood and what you feel most comfortable writing.
JSPerf results
My personal favorite method at the moment, until ES6 becomes the current standard, is the while loop method. In my opinion it is the most readable method of iterating a NodeList.
var els = document.getElementsByTagName('i'), i = 0, el;
while((el = els.item(i++))) el.innerHTML += '*';
<i>Hello</i> <i>foo</i> <i>bar</i> <i>world</i>