Syntax
parent.addEventListener("event", (e) => { if (e.target.matches(selector)) { } })Examples
Understanding Bubbling
An event fired on a child also triggers listeners on its ancestors.
// <div id="outer"><button id="inner">Click</button></div>
document.querySelector("#outer").addEventListener("click", () => {
console.log("Outer div clicked (via bubbling)");
});
document.querySelector("#inner").addEventListener("click", () => {
console.log("Button clicked directly");
});
// Clicking the button logs both messages, in this order:
// "Button clicked directly"
// "Outer div clicked (via bubbling)"Event Delegation
Using one listener on a parent to handle events from many current and future children.
// A single listener handles clicks on ANY .item, even ones added later
document.querySelector("#itemList").addEventListener("click", (event) => {
if (event.target.matches(".item")) {
console.log("Clicked item:", event.target.textContent);
}
});
// Compare to attaching a separate listener to every .item individually,
// which would need to be redone whenever new items are addedstopPropagation()
Preventing an event from continuing to bubble up to ancestors.
document.querySelector("#inner").addEventListener("click", (event) => {
event.stopPropagation(); // the outer div's listener will NOT fire
console.log("Only this listener runs");
});Best practices
- Use event delegation for lists or grids where items are added/removed dynamically - one listener on the parent handles all current and future children
- Use event.target (the actual element clicked) rather than event.currentTarget (the element the listener is attached to) when delegating
- Reserve stopPropagation() for genuine cases where bubbling would cause a real problem - overusing it can break other legitimate listeners expecting the event to bubble
- Combine delegation with matches() or closest() to reliably identify which specific child element triggered the event
At a glance
- Purpose
- Application logic and interaction
- File extension
- .js ยท .mjs
- Runs in
- Browsers and JavaScript runtimes
- Usually used with
- HTML, CSS and Web APIs
Specifications & further reading
Related JavaScript documentation
addEventListener() and the Event Object
addEventListener() attaches a function to run when a specific event occurs on an element, without overwriting any other listeners already attached (unlike the older onclick= style). The callback receives an Event object with details about what happened, including target (the element that triggered it) and methods like preventDefault().Arrow Functions
Arrow functions provide a more concise syntax for writing function expressions in JavaScript. Introduced in ES6, they use the => syntax and have some important differences from regular functions, particularly in how they handle the "this" keyword. Arrow functions are especially useful for callbacks, array methods, and short inline functions.Async/Await
Async/await is modern JavaScript syntax for handling asynchronous operations, making asynchronous code look and behave like synchronous code. The async keyword marks a function as asynchronous, and await pauses execution until a Promise resolves. This approach makes asynchronous code more readable and easier to understand than traditional Promise chains or callbacks.Variables: var, let, const
JavaScript has three ways to declare variables. var is the original way, function-scoped and hoisted with a default value of undefined. let, introduced in ES6, is block-scoped and can be reassigned. const is also block-scoped but cannot be reassigned after its initial value is set. Modern JavaScript strongly favors let and const over var.
addEventListener() attaches a function to run when a specific event occurs on an element, without overwriting any other listeners already attached (unlike the older onclick= style). The callback receives an Event object with details about what happened, including target (the element that triggered it) and methods like preventDefault().Arrow Functions
Arrow functions provide a more concise syntax for writing function expressions in JavaScript. Introduced in ES6, they use the => syntax and have some important differences from regular functions, particularly in how they handle the "this" keyword. Arrow functions are especially useful for callbacks, array methods, and short inline functions.Async/Await
Async/await is modern JavaScript syntax for handling asynchronous operations, making asynchronous code look and behave like synchronous code. The async keyword marks a function as asynchronous, and await pauses execution until a Promise resolves. This approach makes asynchronous code more readable and easier to understand than traditional Promise chains or callbacks.Variables: var, let, const
JavaScript has three ways to declare variables. var is the original way, function-scoped and hoisted with a default value of undefined. let, introduced in ES6, is block-scoped and can be reassigned. const is also block-scoped but cannot be reassigned after its initial value is set. Modern JavaScript strongly favors let and const over var.