Event propagation
Hey ,
I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.
Please upgrade (use this link) access this content.
I'm super eager to help you learn more!
Hey ,
I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.
Please upgrade (use this link) access this content.
I'm super eager to help you learn more!
Three phases occur when an event is fired:
Together, they’re called event propagation.
Here, JavaScript goes through window, document, followed by every element until it reaches the event target.
Event listeners can listen to the capturing phase if you provide it with a third argument, useCapture, which is a boolean.
document.addEventListener('event-name', callback, useCapture)
If useCapture is true, the callback will be called in the capturing phase. If useCapture is false, the callback will not be called in the capturing phase.
To see how the capturing phase works, we can build a demo with three nested <div>, like this:
<div class="box box1">
<span>Box 1</span>
<div class="box box2">
<span>Box 2</span>
<div class="box box3"> <span>Box 3</span> </div>
</div>
</div>
Here, we add an event listener to each element. In each callback, we want to log the eventPhase property. This event phase property tells us which phase we’re in.
eventPhase returns 1eventPhase returns 2eventPhase returns 3const boxes = document.querySelectorAll('.box')
boxes.forEach(box => {
box.addEventListener('click', e => {
console.log(e.eventPhase, e.currentTarget)
}, true)
})
I clicked on .box3 in the gif below.
You can see that the events are fired such in this order:
The target phase comes next. Here, JavaScript reaches the element that fired the event and triggers all event listeners attached to it. The target phase disregards the useCapture flag.
const box3 = document.querySelector('.box3')
box3.addEventListener('click', listener, true)
box3.addEventListener('click', listener)
In the GIF above, you can see all event listeners activate. It doesn’t matter if useCapture is present.
The bubbling phase comes last. Here, JavaScript goes through every HTML Element, starting from the target, back to Window.
Event listeners without the useCapture flag will trigger in this phase.
const boxes = document.querySelectorAll('.box')
boxes.forEach(box => box.addEventListener('click', e => {
console.log(e.eventPhase, e.currentTarget)
}))
I clicked on .box3 in the gif below.
You can see that events trigger in the following sequence:
Events that bubble have a bubbles property set to true. An example is a click event:
Some events don’t bubble. Examples of these events are focus and blur.
If two listeners are attached to the same element, listener that is attached first fires first.
const button = document.querySelector('button')
button.addEventListener('click', e => console.log('First event'))
button.addEventListener('click', e => console.log('Second event'))
If you want to prevent an event from bubbling, you can use stopPropagation or stopImmediatePropagation.
stopPropagation prevents events from bubbling upwardsstopImmediatePropagation prevents events from bubbling upwards, and also prevents subsequent events on the listening element from firing.// Stopping propagation
const box2 = document.querySelector('.box2')
const box3 = document.querySelector('.box3')
box2.addEventListener('click', e => console.log('box 2 clicked!'))
box3.addEventListener('click', e => e.stopPropagation())
Events from .box3 will not bubble to .box2 because we called stopPropagation in .box3.
Familiarize yourself with the sequence of events that occur.
Answer these questions:
document.body.addEventListener('click', ev => { /* Do something */}, true)
document.body.addEventListener('click', ev => { /* Do something */})
useCapture set to true.useCapture (or useCapture set to false).e.stopPropagation().