11 Ways of iterating over a NodeList

JoelBonetR 🥇 - Nov 6 '20 - - Dev Community

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');
Enter fullscreen mode Exit fullscreen mode

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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'));
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Check it:

  console.log(myNodeList); // NodeList
  console.log(myArrayFromNodeList); // Array of Nodes
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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);
  });
}
Enter fullscreen mode Exit fullscreen mode

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

And, of course two more classics, the while loop:

let i = 0;
while(elements[i] !== undefined) {
  console.log(elements[i]);
  ++i;
}
Enter fullscreen mode Exit fullscreen mode

And the do...while!

i = 0;
do {
  console.log(elements[i]);
  ++i;
} while (elements[i] !== undefined);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .