Let’s look at some options for iterating over a NodeList
, as you get back from running a document.querySelectorAll
.
We’ve written an updated article about this: A Bunch of Options for Looping Over querySelectorAll NodeLists.
Not all browsers support forEach on NodeLists, but for those that do:
buttons.forEach((button) => {
button.addEventListener('click', () => {
console.log("forEach worked");
});
});
Here’s a tricky way to get around that with a bit deeper browser support.
var divs = document.querySelectorAll('div');
[].forEach.call(divs, function(div) {
// do whatever
div.style.color = "red";
});
Fair warning, Todd Motto explains why this method is a rather hacky, detailing over 10 problems with it.
You could also use a classic for loop:
var divs = document.querySelectorAll('div'), i;
for (i = 0; i < divs.length; ++i) {
divs[i].style.color = "green";
}
Todd’s suggestion is to make your own method:
// forEach method, could be shipped as part of an Object Literal/Module
var forEach = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]); // passes back stuff we need
}
};
// Usage:
// optionally change the scope as final parameter too, like ECMA5
var myNodeList = document.querySelectorAll('li');
forEach(myNodeList, function (index, value) {
console.log(index, value); // passes index + value back!
});
You can also spread the list yourself, which then would give you access to other array methods while you’re at it.
[...buttons].forEach((button) => {
button.addEventListener('click', () => {
console.log("spread forEach worked");
});
});
There are also for..of loops. Firefox was the first to support this but support has gotten pretty good:
for (const button of buttons) {
button.addEventListener('click', () => {
console.log("for .. of worked");
});
}
This is pretty intense (probably dangerous and not recommended) but you could make NodeList have the same forEach function as Array does, then use it.
NodeList.prototype.forEach = Array.prototype.forEach;
var divs = document.querySelectorAll('div').forEach(function(el) {
el.style.color = "orange";
})
There is a bit more information in the MDN article.
The safer way to do the last version and co-opt the Array method would be to do something like this:
Of course, you still need to make sure you have a polyfill for Array.forEach if you’re targeting older browsers.
Just note, some people discourage the
[].forEach.call(..)
pattern in favour of the classic loop.Hey this saved me some time – I was curious why I couldn’t loop over with forEach. Thanks!
there is a pretty nice function that gives you a array:
function getDomNodeArray(selector) {
// get the elements as a DOM collection
var elemCollection = document.querySelectorAll(selector);
// coerce the DOM collection into an array
var elemArray = Array.prototype.slice.apply(elemCollection);
return elemArray;
};
That’s exactly what I use. I use an extra optional argument, which is the “context” or the element in which the querySelectorAll() is called (default is document)
Then usage may be:
ETC…. It is very powerful yet very simple. It has also a one-line-form:
I just wanted to share this “discovery”….
ES2015 variant using spread operator:
This is my preference.
If you’re using ES6, you should also be able to do this:
Array.from(querySelectorAll('img')).forEach(img => doStuff);
Clean and simple.
clever.
clean and simple and elegant.
Just want to note
.from
currently doesn’t have Internet Explorer support – MDNOverload the NodeList, then no headache :)
This is should be the fastest way:
nl equal to a NodeLIst
A different aproach, a little more straightforward:
(Tested.)
You can also do
or you could just use
Array.from
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromthen it would actually be an Array.
What about this:
I just noticed that
NodeList.prototype.forEach()
ist obviously now party of the official JS-API in Chrome & Firefox. See https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach for details and the official polyfill.Now we have also Array.from() :)
so I run a few tests to see how good are these suggestions are, and I came to the conclusion that the fastest way (execution time) to cycle node lists, is to just use Array.prototype.forEach.call… Todd’s solution took 7.9ms on an empty nodelist of 10’000 items to perform, the native solution took 0.08ms on the same nodelist… so I would suggest Todd and you guys to not make your own methods to cycle nodelists