Iterating through NodeList
Asked Answered
S

2

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);
});

Slavophile answered 11/12, 2019 at 17:22 Comment(1)
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.Fretwell
B
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 => {})
Broussard answered 11/12, 2019 at 17:24 Comment(5)
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.Fretwell
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!Broussard
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?Fretwell
.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"Fretwell
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.callBroussard
P
0

The following works for me just fine:

[].map.call(nodeList, item => {})
Parturifacient answered 22/11, 2023 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.