Event Delegation
Event delegation is a highly efficient design pattern in JavaScript where you attach a single event listener to a parent element to manage events for all of its current—and future—child elements. It works because of event bubbling: an event fired on a child element bubbles up through its ancestors, so a listener on the parent can catch it.
<ul id="fruit-list">
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
// Without delegation: one listener per <li>, and new <li>s added later won't have a listener at all
document.querySelectorAll("#fruit-list li").forEach((li) => {
li.addEventListener("click", () => console.log(li.textContent));
});
// With delegation: a single listener on the parent handles all children
document.getElementById("fruit-list").addEventListener("click", (event) => {
if (event.target.tagName === "LI") {
console.log(event.target.textContent);
}
});
Why it’s useful
- Performance: One listener instead of hundreds/thousands — less memory, faster setup.
- Works with dynamically added elements: New children added after the listener was attached are still handled, since the listener lives on the parent, not the child.
const list = document.getElementById("fruit-list");
list.addEventListener("click", (event) => {
if (event.target.tagName === "LI") {
console.log("Clicked:", event.target.textContent);
}
});
// This new <li>, added after the listener was set up, still works
const newFruit = document.createElement("li");
newFruit.textContent = "Dragonfruit";
list.appendChild(newFruit);
Targeting the right element
event.target is the actual element that was clicked, which may be a descendant of the element you meant to match (e.g. an <img> inside a <button>). Use closest() to find the intended element reliably.
document.getElementById("toolbar").addEventListener("click", (event) => {
const button = event.target.closest("button");
if (!button) return; // click didn't happen inside a button
console.log("Button clicked:", button.dataset.action);
});