Add an event listener to multiple elements in JavaScript
By Flavio Copes
Learn how to add the same event listener to multiple elements in JavaScript, either by looping over querySelectorAll with forEach or by using event bubbling.
You can attach the same event listener to multiple elements in 2 ways: loop over the elements and call addEventListener() on each, or attach a single listener to a parent element and rely on event bubbling.
In JavaScript you add an event listener to a single element using this syntax:
document.querySelector('.my-element').addEventListener('click', event => {
//handle click
})
But how can you attach the same event to multiple elements?
Using a loop
The loop is the simplest one conceptually.
You can call querySelectorAll() on all elements with a specific class, then use forEach() to iterate on them:
document.querySelectorAll('.some-class').forEach(item => {
item.addEventListener('click', event => {
//handle click
})
})
querySelectorAll() returns a NodeList, which has a forEach() method built in, so you don’t need to convert it to an array first.
If you don’t have a common class for your elements you can build an array on the fly:
[document.querySelector('.a-class'), document.querySelector('.another-class')].forEach(item => {
item.addEventListener('click', event => {
//handle click
})
})
Using event bubbling
Another option is to rely on event bubbling and attach the event listener on a parent, like the body element.
When you click an element, the event bubbles up through all its ancestors. A listener on body sees every click on the page, and event.target tells you the element that was clicked. So you can check if the target is one of the elements you care about:
const element1 = document.querySelector('.a-class')
const element2 = document.querySelector('.another-class')
document.body.addEventListener('click', event => {
if (event.target !== element1 && event.target !== element2) {
return
}
//handle click
})
This technique is called event delegation and it has a nice bonus: it also works for elements added to the page later, after the listener was attached. The loop approach only covers the elements that existed when the loop ran.
Watch out for nested elements
There’s a pitfall with the bubbling approach. event.target is the innermost element that was clicked.
If your element contains children, say a button with an icon inside, clicking the icon makes event.target the icon, not the button. The strict !== check fails and your handler never runs.
The fix is to use closest(), which walks up from the target looking for a matching ancestor:
document.body.addEventListener('click', event => {
if (!event.target.closest('.a-class')) {
return
}
//handle click
})
Now clicks anywhere inside the element, including its children, are handled correctly.
Related posts about js: