In JavaScript, events propagate through event bubbling (from innermost to outermost) and event capturing (opposite direction). Bubbling triggers handlers on child elements first, while capturing starts from the outermost element.
Both allow event handling at different DOM levels. Events are generally processed in three phases in the DOM. They are the capturing, the target, and the bubbling phase.
The addEventListener()
method is the recommended way to register an event listener. It gives you finer-grained control of the phase when the listener is activated (capturing vs. bubbling). It works on any event target, not just HTML or SVG elements.
Event bubbling is a concept in JavaScript where an event that is triggered on a specific element propagates, or “bubbles up,” through the ancestor elements in the DOM tree.
This means that if an event occurs on an element, it first triggers any event handlers on that element, then triggers handlers on its parent, then its grandparent, and so on, all the way up to the root of the DOM tree.
Consider the following HTML:
<div id="parent">
<button id="child">Click me</button>
</div>
add event listeners to both:
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked');
});
document.getElementById('child').addEventListener('click', function(event) {
console.log('Child clicked');
});
When you click the button (#child), the console output will be:
Child clicked
Parent clicked
If you want to prevent an event from propagating up the DOM tree, you can use the event.stopPropagation()
method within your event handler:
document.getElementById('child').addEventListener('click', function(event) {
console.log('Child clicked');
event.stopPropagation(); // Stop the event from bubbling up
});
Now, when you click the button, the output will be:
Child clicked
The “Parent clicked” message will no longer appear because the event propagation has been stopped.
Event capturing, also known as event trickling, is a phase in the event propagation process in JavaScript where the event starts from the root element and travels down to the target element. This is the opposite of event bubbling.
To set up an event listener for the capture phase, you can pass a third parameter (or options object) to the addEventListener()
method and set it to true
.
addEventListener(type, listener)
addEventListener(type, listener, options)
addEventListener(type, listener, useCapture)
Consider the following HTML:
<div id="grandparent">
<div id="parent">
<button id="child">Click me</button>
</div>
</div>
and adding event listeners:
document.getElementById('grandparent').addEventListener('click', function() {
console.log('Grandparent clicked (capturing)');
}, true); // Capturing phase
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked (capturing)');
}, true); // Capturing phase
document.getElementById('child').addEventListener('click', function() {
console.log('Child clicked (capturing)');
}, true); // Capturing phase
document.getElementById('child').addEventListener('click', function() {
console.log('Child clicked (bubbling)');
}); // Bubbling phase
When you click the button (#child), the console output will be:
Grandparent clicked (capturing)
Parent clicked (capturing)
Child clicked (capturing)
Child clicked (bubbling)
Event listeners in the capturing phase are called before event listeners in any non-capturing phases.