5

I'm looking for a future proof way to iterate through a NodeList (ie from element.querySelectorAll(selector)) as well as be cross-browser compatible. Previously I'd been using the ES6 Spread functionality, however IE doesn't support spreading so I used a shim for it instead. I felt that was a bit overkill and inefficient. I then came across the hack Array.prototype.forEach.call. It works, but looks smelly to me.

What is the best method to iterate over a NodeList?

I'm partial to backwards compatibility and clean smelling code, but if your answer also works for any of the other criteria below, that would be appreciated.

  1. Readability
  2. Future Proofing
  3. Speed

I've looked at JavaScript iterate through NodeList, which goes over a few methods. However, there's no concern over readability, compatibility, etc. Just if it works.

A few methods I've come across are:

const elems = document.querySelectorAll('img');

[].forEach.call(elems, function (o) { console.log(o) });

[...elems].foreach(function (o) { console.log(o) });

for (var i = 0; i < elems.length; i++) {
    console.log(elems[i]);
}

for (var i = elems.length - 1; i >= 0; i--) {
    console.log(elems[i]);
}

// User-defined
var forEach = function (array, callback, scope) {
    for (var i = 0; i < array.length; i++) {
        callback.call(scope, i, array[i]);
    }
};
forEach(elems, function (index, value) {
    console.log(index, value);
});

KGlasier
  • 155
  • 1
  • 6
  • 1. most are readable. It depends on what you're comfortable with. The simplest is just a `for` loop but you can also go with `[].forEach.call` which I'd consider idiomatic. 2. I don't expect *any* of these to be removed in the future, so I'm not sure why you want it more "future proof" than what already works today. If it works on IE and modern browsers, it'd work years from now. 3. Speed is likely not a concern. I doubt any common method will grind your application to a halt. Simple iteration is rarely a bottleneck - performance problems are usually elsewhere. – VLAZ Dec 11 '19 at 17:27

1 Answers1

6

I recommend to use the MDN's Array.from polyfill

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Polyfill

Array.from(nodeList).forEach(node => {})
TKoL
  • 13,158
  • 3
  • 39
  • 73
  • Or just `[].prototype.slice.call(nodeList)` No need for a full polyfill where the old reliable for converting array-likes still works. Unless you plan on using `Array.from` more, including other types of invocations for it, I suppose. – VLAZ Dec 11 '19 at 17:29
  • I do it for readability's sake personally. Someone else looks at my code, `[].prototype.slice.call` is kinda obscure compared to `Array.from()` imo. Hell, even if it's just me looking at my own code! – TKoL Dec 11 '19 at 17:33
  • It's a common idiom by now. Sure, an outsider might be confused but...I guess, they would be *completely* new to JS. I'm not sure writing code for people not versed in the language is a good way to go, as it cuts you off from a lot of stuff that *should* be common but you can't use because you might confuse other people. Should we disallow using `document.querySelector` since somebody might now know what CSS selectors are? – VLAZ Dec 11 '19 at 17:38
  • `.slice` is used to copy arrays: `arr2 = arr1.slice()`. It's been a long standing copy mechanism. `call` is how you call a method with a new target. So, it's describing itself as "make an array copy using this as target" – VLAZ Dec 11 '19 at 17:46
  • No, I know what it all does, that was a rhetorical question to describe how it's not obvious what it does just from the words. I'm just saying the full string `[].prototype.slice.call` literally doesn't have a single word in it that describes what it does. "make an array copy using this as target" -- none of those words are in `[].prototype.slice.call` – TKoL Dec 11 '19 at 17:54