What is event delegation, and how do event bubbling and capturing work?
A DOM event travels in three phases: it captures down from the window to the target, fires at the target, then bubbles back up to the window. addEventListener attaches to the bubbling phase by default; passing true or { capture: true } attaches to the capturing phase instead.
Event delegation uses bubbling deliberately. Instead of attaching a listener to every item, you attach one to a common ancestor and inspect event.target:
list.addEventListener('click', (e) => {
const item = e.target.closest('li');
if (!item || !list.contains(item)) return;
handle(item.dataset.id);
});Why it is worth doing:
- One listener instead of hundreds — less memory and a faster initial render.
- It works for elements added later, with no need to rebind after every update.
Note: Know the difference between stopPropagation, which halts the journey up the tree, and preventDefault, which cancels the browser's default action such as following a link. They are unrelated, and reaching for stopPropagation is usually a sign that something else is wrong.





