Instead of attaching a handler to every child, attach one to the parent and let events bubble up. Fewer listeners, same behavior, and it works for elements that don't exist yet.
Click anywhere in the nested boxes. The event starts at the innermost element and ripples outward through every ancestor. That's bubbling — and it's why delegation works.
click anywhere
Both lists behave identically. Click any item — it flashes. The difference is invisible to the user but real in memory: 50 handlers vs 1. With 10,000 items the gap is dramatic.
handlers: 50
handlers: 1
Add items after the page loads. A single parent handler catches clicks on elements that didn't exist when the handler was attached. No re-binding needed.
Most people only need to know bubble exists and capture is opt-in. Events go down (capture), hit the target, then go back up (bubble). Delegation listens during the bubble phase by default.
Event delegation in vanilla JS
const list = document.querySelector('.item-list') list.addEventListener('click', (e)=> { const item = e.target.closest('.item') if (!item) return // item is the clicked element handleItemClick(item) })
In React, synthetic events already delegate to the root. You rarely need manual delegation — but understanding bubbling is still essential for stopPropagation, portals, and debugging event order.
O(1) listeners instead of O(n)