Event bubbling and event capturing
By Flavio
Published · Updated
Tagged: JavaScript
Learn how event bubbling and capturing control the order DOM event handlers fire in JavaScript, and how to switch to capturing with addEventListener.
Bubbling and capturing are the 2 models that DOM events use to propagate.
Suppose your DOM structure is
<div id="container">
<button>Click me</button>
</div>
You want to track when users click on the button, and you have 2 event listeners, one on button, and one on #container.
Remember, a click on a child element will always propagate to its parents, unless you stop the propagation. event.stopPropagation() stops the event from reaching other elements. I wrote more about that in preventDefault vs stopPropagation.
Those event listeners will be called in order, and this order is determined by the event bubbling/capturing model used.
Bubbling means that the event propagates from the item that was clicked (the child) up to all its parent tree, starting from the nearest one.
In our example, the handler on button will fire before the #container handler.
Let’s register both and click the button:
document.querySelector('button').addEventListener('click', () => {
console.log('button')
})
document.getElementById('container').addEventListener('click', () => {
console.log('container')
})
The console prints button, then container. That is bubbling.
Capturing is the opposite: the outer event handlers are fired before the more specific handler, the one on button.
Listeners are registered for the bubbling phase by default. Some events never bubble: focus, blur, mouseenter, mouseleave, load on elements, scroll on elements. Check event.bubbles. focusin and focusout are the bubbling versions of focus and blur.
This is why event delegation works for click: the event bubbles, so one parent listener can handle many children.
You can choose to adopt event capturing by passing a third argument to addEventListener. Today you usually pass an options object, { capture: true } (supported since Chrome 49, Firefox 49, Safari 10). true is the older shorthand:
document.getElementById('container').addEventListener(
'click',
() => {
//container clicked
},
true
)
Same thing with the options object:
document.getElementById('container').addEventListener(
'click',
() => {
//container clicked
},
{ capture: true }
)
If the container uses { capture: true } and the button stays on the default (bubble), a click on the button prints container, then button.
The order is: capturing from window down to the target’s parent, then the target phase, then bubbling back up to window.
First all capturing handlers run. Then the target handlers. Then all the bubbling handlers.
Handlers on the target itself run in the order you registered them. capture does not reorder two listeners on the same element.
Want me to talk about your product? You can sponsor this site.
Related posts about js: