One Handler for All
Event bubbling unlocks one of the most powerful and efficient patterns in front-end development: Event Delegation.
The Problem
Imagine a menu with 100 items. Attaching a separate click listener to each item creates 100 event handlers — a waste of memory. What if items are added dynamically? You'd have to add a new listener every time.
The Solution: Delegation
Instead of attaching handlers to each child, you attach one handler to their common ancestor. Then, inside the handler, you check event.target to see exactly which child was clicked.
const menu = document.querySelector('.menu');
menu.addEventListener('click', function(event) {
// Find the closest <li> ancestor of what was clicked
const item = event.target.closest('li');
if (!item) return; // Clicked outside any list item? Do nothing.
console.log('You clicked: ' + item.dataset.action);
});
Why use closest()?
If a list item has an icon inside it: <li><img>Save</li>, clicking the icon makes event.target the <img>, not the <li>. closest('li') safely walks up the tree until it finds the right element.
Event delegation saves memory, simplifies code, and automatically works for dynamically added elements — no new listeners needed!