A Bunch of Options for Looping Over querySelectorAll NodeLists
A common need when writing vanilla JavaScript is to find a selection of elements in the DOM and loop over them. For example, finding instances of a button and attaching a click handler to them.
const buttons = document.querySelectorAll(".js-do-thing");
// There could be any number of these!
// I need to loop over them and attach a click handler.
There are SO MANY ways to go about it. Let’s go through them.
forEach
forEach
is normally for arrays, and interestingly, what comes back from querySelectorAll
is not an array but a NodeList. Fortunately, most modern browsers support using forEach
on NodeLists anyway.
buttons.forEach((button) => {
button.addEventListener('click', () => {
console.log("forEach worked");
});
});
If you’re worried that forEach
might not work on your NodeList, you could spread it into an array first:
[...buttons].forEach((button) => {
button.addEventListener('click', () => {
console.log("spread forEach worked");
});
});
But I’m not actually sure if that helps anything since it seems a bit unlikely there are browsers that support spreads but not forEach
on NodeLists. Maybe it gets weird when transpiling gets involved, though I dunno. Either way, spreading is nice in case you want to use anything else array-specific, like .map()
, .filter()
, or .reduce()
.
A slightly older method is to jack into the array’s natural forEach
with this little hack:
[].forEach.call(buttons, (button) => {
button.addEventListener('click', () => {
console.log("array forEach worked");
});
});
Todd Motto once called out this method pretty hard though, so be advised. He recommended building your own method (updated for ES6):
const forEach = (array, callback, scope) => {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]);
}
};
…which we would use like this:
forEach(buttons, (index, button) => {
console.log("our own function worked");
});
for .. of
Browser support for for .. of
loops looks pretty good and this seems like a super clean syntax to me:
for (const button of buttons) {
button.addEventListener('click', () => {
console.log("for .. of worked");
});
}
Make an array right away
const buttons = Array.prototype.slice.apply(
document.querySelectorAll(".js-do-thing")
);
Now you can use all the normal array functions.
buttons.forEach((button) => {
console.log("apply worked");
});
Old for loop
If you need maximum possible browser support, there is no shame in an ancient classic for
loop:
for (let i = 0; i < buttons.length; ++i) {
buttons[i].addEventListener('click', () => {
console.log("for loop worked");
});
}
Libraries
If you’re using jQuery, you don’t even have to bother….
$(".buttons").on("click", () => {
console.log("jQuery works");
});
If you’re using a React/JSX setup, you don’t need think about this kind of binding at all.
Lodash has a _.forEach
as well, which presumably helps with older browsers.
_.forEach(buttons, (button, key) => {
console.log("lodash worked");
});
Poll
Twitter peeps:
const els = document.querySelectorAll(“.foo”);
// which loop do you use? one of these? other?
— Chris Coyier (@chriscoyier) November 7, 2018
Also here’s a Pen with all these options in it.
The post A Bunch of Options for Looping Over querySelectorAll NodeLists appeared first on CSS-Tricks.