Hint
3 phases: capture (down), target, bubble (up) — addEventListener default is bubble
When an event fires, it passes through 3 phases in the DOM tree:
// Capture phase listener (3rd arg = true, or { capture: true })
document.body.addEventListener('click', () => console.log('body capture'), true);
// Bubble phase listener (default)
document.body.addEventListener('click', () => console.log('body bubble'));
button.addEventListener('click', () => console.log('button'));
// Click the button:
// body capture (capturing, going down)
// button (at target)
// body bubble (bubbling, going up)
// Stop bubbling
button.addEventListener('click', (e) => {
e.stopPropagation(); // stops bubble — body bubble won't fire
// e.stopImmediatePropagation() — also blocks same-element listeners
});
Events that don't bubble: focus, blur, scroll, mouseenter, mouseleave — use focusin/focusout for delegation instead.