Have you ever thank on how many ways we can iterate over a NodeList? I myself use a single workaround for a job but I was thinking about and I realized there's much more. Let’s look at some options
Getting our NodeList:
const elements = document.querySelectorAll('.selector');
Let's begin with the classic forEach
Note that not all browsers support forEach on NodeLists, but for those that it works:
elements.forEach((element) => {
element.addEventListener('click', () => {
console.log("iteration worked", element);
});
});
Another way to use forEach with call method, can seem a bit tricky but it works and has more wide browser support:
[].forEach.call(elements, function(element) {
console.log("iteration worked", element);
});
Take care as this last way to do it access the created empty array's prototype method and using call allows the NodeList to take advantage, but this even it can work is a bad practice, let's see the following example and why it's bad:
NodeList.prototype.forEach = Array.prototype.forEach;
This will extend the existing DOM functionality through prototypes. This is considered as a bad practice as it can lead to tones of issues.
Some of the issues are that we are caching the selector, but we’re not caching the array or even what the loop is doing, which means we can’t reuse the method.
It also lacks of Array methods (we only extended forEach and of course it's not recommended to extend all of them even you do it only when needed).
NodeLists and Arrays are different tools so we can "cast" when needed:
var myArrayFromNodeList = [].slice.call(document.querySelectorAll('.selector'));
But that's another Array.prototype hack and I don't recommend it either. By the way it does not work with IE (who cares about IE anyway?)
How can we achieve that on a better way?
var myNodeList = document.querySelectorAll('.selector');
var myArrayFromNodeList = []; // empty at first
for (var i = 0; i < myNodeList.length; i++) {
myArrayFromNodeList.push(myNodeList[i]); // push it, babe
}
Check it:
console.log(myNodeList); // NodeList
console.log(myArrayFromNodeList); // Array of Nodes
OK let's go back to the main thread with the for loop:
for (i = 0; i < elements.length; ++i) {
console.log("iteration worked", elements[i]);
}
The forEach... again?
// 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]);
}
};
// optionally change the scope as final parameter too
forEach(elements, function (index, value) {
console.log(index, value);
});
Disclaimer: please this is for educational purposes but don't name your vars with forEach or any other built-in method.
Now it's the time to talk about for...of loops:
for (const element of elements) {
element.addEventListener('click', () => {
console.log("iteration worked", elements);
});
}
Well if you thank this is the end... it's not! we've not talked about the ES6 Spread Operator!
[...elements].forEach((element) => {
element.addEventListener('click', () => {
console.log("spread forEach worked", element);
});
});
And, of course two more classics, the while loop:
let i = 0;
while(elements[i] !== undefined) {
console.log(elements[i]);
++i;
}
And the do...while!
i = 0;
do {
console.log(elements[i]);
++i;
} while (elements[i] !== undefined);
Remember that the do...while will always run once before checking the condition so you need to ensure it will be true at least once unless you want an error which can be bad or worse depending on the stuff you do in between the brackets. For example
i = undefined;
do {
console.log(elements[i]);
++i;
} while (elements[i] !== undefined);
will return a NaN
or eventually undefined
And that's all, hope you learnt about what you can do and about what you must don't. Do you know another way to iterate over NodeLists?
Bonus:
As @jwp suggested:
Array.from(nodelist)
Once we created an array from a nodelist we can iterate over it through any array iteration method.
Do you have any question? Let me know in the comment section 😄
Best regards,
Joel