Add a click event to querySelectorAll elements
By Flavio Copes
Learn how to attach a click event listener to every element returned by document.querySelectorAll() by looping over the NodeList with a for..of loop.
You can add an event listener to all the elements returned by a document.querySelectorAll() call by iterating over those results using the for..of loop:
const buttons = document.querySelectorAll('#select .button')
for (const button of buttons) {
button.addEventListener('click', function (event) {
//...
})
}
Why do you need a loop?
addEventListener() is a method of a single DOM element. It attaches the listener to that one element, and nothing else.
document.querySelectorAll() returns a collection of elements. There is no way to call addEventListener() on the collection itself and have it apply to everything inside. So we loop, and attach the listener to each element, one at a time.
Note that document.querySelectorAll() does not return an array, but a NodeList object.
A NodeList looks like an array, but it’s not one. It has a length property, and you can iterate it with for..of or with its own forEach() method:
document.querySelectorAll('#select .button').forEach((button) => {
button.addEventListener('click', function (event) {
//...
})
})
If you need real array methods like map() or filter(), you can transform it to an array with Array.from() first.
How do you know which element was clicked?
The same listener function runs for every element, so inside it you often need to know which one triggered the event.
Use event.currentTarget:
for (const button of buttons) {
button.addEventListener('click', function (event) {
console.log(event.currentTarget.textContent)
})
}
event.currentTarget is the element the listener was attached to. event.target is the element that was actually clicked, which might be a child, like an icon inside the button.
Be careful with elements added later
The loop only attaches listeners to the elements that exist when it runs.
If your app adds more .button elements to the page afterwards, those new elements have no listener, and clicking them does nothing. This is a very common source of “my click handler stopped working” bugs.
The fix is event delegation: attach a single listener to a parent element that’s always in the page, and check what was clicked:
document.querySelector('#select').addEventListener('click', (event) => {
if (event.target.closest('.button')) {
//...
}
})
Clicks bubble up from the clicked element to its ancestors, so the parent catches clicks on every button inside it, including buttons added after the page loaded.
Related posts about platform: