What Is Event Bubbling and Capturing

What is event bubbling and capturing?

Event bubbling and capturing are two ways of event propagation in the HTML DOM API, when an event occurs in an element inside another element, and both elements have registered a handle for that event. The event propagation mode determines in which order the elements receive the event.

With bubbling, the event is first captured and handled by the innermost element and then propagated to outer elements.

With capturing, the event is first captured by the outermost element and propagated to the inner elements.

Capturing is also called "trickling", which helps remember the propagation order:

trickle down, bubble up

Back in the old days, Netscape advocated event capturing, while Microsoft promoted event bubbling. Both are part of the W3C Document Object Model Events standard (2000).

IE < 9 uses only event bubbling, whereas IE9+ and all major browsers support both. On the other hand, the performance of event bubbling may be slightly lower for complex DOMs.

We can use the addEventListener(type, listener, useCapture) to register event handlers for in either bubbling (default) or capturing mode. To use the capturing model pass the third argument as true.

Example

<div>
<ul>
<li></li>
</ul>
</div>

In the structure above, assume that a click event occurred in the li element.

In capturing model, the event will be handled by the div first (click event handlers in the div will fire first), then in the ul, then at the last in the target element, li.

In the bubbling model, the opposite will happen: the event will be first handled by the li, then by the ul, and at last by the div element.

For more information, see

  • Event Order on QuirksMode
  • addEventListener on MDN
  • Events Advanced on QuirksMode

In the example below, if you click on any of the highlighted elements, you can see that the capturing phase of the event propagation flow occurs first, followed by the bubbling phase.

var logElement = document.getElementById('log');

function log(msg) {

logElement.innerHTML += ('<p>' + msg + '</p>');

}

function capture() {

log('capture: ' + this.firstChild.nodeValue.trim());

}

function bubble() {

log('bubble: ' + this.firstChild.nodeValue.trim());

}

function clearOutput() {

logElement.innerHTML = "";

}

var divs = document.getElementsByTagName('div');

for (var i = 0; i < divs.length; i++) {

divs[i].addEventListener('click', capture, true);

divs[i].addEventListener('click', bubble, false);

}

var clearButton = document.getElementById('clear');

clearButton.addEventListener('click', clearOutput);
p {

line-height: 0;

}

div {

display:inline-block;

padding: 5px;

background: #fff;

border: 1px solid #aaa;

cursor: pointer;

}

div:hover {

border: 1px solid #faa;

background: #fdd;

}
<div>1

<div>2

<div>3

<div>4

<div>5</div>

</div>

</div>

</div>

</div>

<button id="clear">clear output</button>

<section id="log"></section>

Bubbling and Capturing events order at target

You are correct. When an event listener is attached to an element, and the element is the target, it does not matter whether whether the event listener is set to activate during capturing or during bubbling. The official specification describes it here.


  1. For each listener in listeners, whose removed is false:

  2. If phase is "capturing" and listener’s capture is false, then continue.

  3. If phase is "bubbling" and listener’s capture is true, then continue.

  4. (eventually invoke attached listener)

When at the target, the phase is neither capturing nor bubbling, as shown by how your getPhaseStr function parses the eventPhase. So neither 3. continue nor 4. continue are activated, so the listener eventually gets invoked, in the order in which the listeners were attached. (The listener order is preserved in the "event listener list", a list of listeners attached to a particular element)

b1.addEventListener('click', () => console.log('bubbling'));
b1.addEventListener('click', () => console.log('capturing'), true);

b2.addEventListener('click', () => console.log('capturing'), true);
b2.addEventListener('click', () => console.log('bubbling'));
<button id="b1">click</button>
<button id="b2">click</button>

Event Handlers and Listeners & Event Bubbling and Event Capturing

Event Handlers / Event Listeners

There's no difference between an Event Listener and an Event Handler - for all practical purposes, they're the same thing. But, it may help to think about them differently:

I can define a single "handler" ...

function myHandler( e ){ alert('Event handled'); }

... and attach it to multiple elements using 'addEventListener':

elementA.addEventListener( 'click', myHandler );
elementB.addEventListener( 'click', myHandler, true );

Or, I can define my "handler" as a closure within 'addEventListener':

elementC.addEventListener( 'click', function( e ){ alert('Event Handled'); } );

Event Capturing / Event Bubbling

You can think of Event Capturing as the opposite of Event Bubbling - or as the two halves of the event lifecycle. DOM elements can be nested (of course). An event first CAPTURES from the outermost parent to the innermost child, and then BUBBLES from the innermost child to the outermost parent.

You may have noticed that in my example above, the listener attached to elementB has an additional parameter - true - that is not passed to elementA. This tells the listener to respond to the event on the CAPTURE phase, whereas it would respond on the BUBBLE phase by default. Try this at jsfiddle.net:

Say we have 3 nested div elements:

<div id="one">
1
<div id="two">
2
<div id="three">
3
</div>
</div>
</div>

... and we attach a 'click' handler to each:

document.getElementById('one').addEventListener( 'click', function(){ alert('ONE'); } );
document.getElementById('two').addEventListener( 'click', function(){ alert('TWO'); }, true );
document.getElementById('three').addEventListener( 'click', function(){ alert('THREE') } );

If you click '1', you'll see an alert box with the text 'ONE'.

If you click '2', you'll see an alert box 'TWO', followed by an alert box 'ONE' (because 'two' is fired first during the CAPTURE PHASE, and 'one' is fired on the way back up during the BUBBLE PHASE)

If you click '3', you'll see an alert box 'TWO' (CAPTURED), followed by an alert box 'THREE' (BUBBLED), followed by an alert box 'ONE' (BUBBLED).

Clear as mud, right?

What is event bubbling and capturing?

Event bubbling and capturing are two ways of event propagation in the HTML DOM API, when an event occurs in an element inside another element, and both elements have registered a handle for that event. The event propagation mode determines in which order the elements receive the event.

With bubbling, the event is first captured and handled by the innermost element and then propagated to outer elements.

With capturing, the event is first captured by the outermost element and propagated to the inner elements.

Capturing is also called "trickling", which helps remember the propagation order:

trickle down, bubble up

Back in the old days, Netscape advocated event capturing, while Microsoft promoted event bubbling. Both are part of the W3C Document Object Model Events standard (2000).

IE < 9 uses only event bubbling, whereas IE9+ and all major browsers support both. On the other hand, the performance of event bubbling may be slightly lower for complex DOMs.

We can use the addEventListener(type, listener, useCapture) to register event handlers for in either bubbling (default) or capturing mode. To use the capturing model pass the third argument as true.

Example

<div>
<ul>
<li></li>
</ul>
</div>

In the structure above, assume that a click event occurred in the li element.

In capturing model, the event will be handled by the div first (click event handlers in the div will fire first), then in the ul, then at the last in the target element, li.

In the bubbling model, the opposite will happen: the event will be first handled by the li, then by the ul, and at last by the div element.

For more information, see

  • Event Order on QuirksMode
  • addEventListener on MDN
  • Events Advanced on QuirksMode

In the example below, if you click on any of the highlighted elements, you can see that the capturing phase of the event propagation flow occurs first, followed by the bubbling phase.

var logElement = document.getElementById('log');

function log(msg) {

logElement.innerHTML += ('<p>' + msg + '</p>');

}

function capture() {

log('capture: ' + this.firstChild.nodeValue.trim());

}

function bubble() {

log('bubble: ' + this.firstChild.nodeValue.trim());

}

function clearOutput() {

logElement.innerHTML = "";

}

var divs = document.getElementsByTagName('div');

for (var i = 0; i < divs.length; i++) {

divs[i].addEventListener('click', capture, true);

divs[i].addEventListener('click', bubble, false);

}

var clearButton = document.getElementById('clear');

clearButton.addEventListener('click', clearOutput);
p {

line-height: 0;

}

div {

display:inline-block;

padding: 5px;

background: #fff;

border: 1px solid #aaa;

cursor: pointer;

}

div:hover {

border: 1px solid #faa;

background: #fdd;

}
<div>1

<div>2

<div>3

<div>4

<div>5</div>

</div>

</div>

</div>

</div>

<button id="clear">clear output</button>

<section id="log"></section>

Event Capturing vs Event Bubbling

In the past it was a platform issue, Internet Explorer had a bubbling model, and Netscape was more about capturing (yet supported both).

The W3C model calls for you be able to choose which one you want.

I think bubbling is more popular because, as stated there are some platforms that only support bubbling...and it sort of makes sense as a "default" mode.

Which one you choose is largely a product of what you are doing and what makes sense to you.

How does event bubbling work on event handling?

When an event is being sent to an element, it descends the document tree in the capture phase until it reaches the target. Then, if it’s a bubbling event, it bubbles back up.

From 2.1 Introduction to “DOM Events” in the DOM standard:

When an event is dispatched to an object that participates in a tree (e.g. an element), it can reach event listeners on that object’s ancestors too. First all object’s ancestor event listeners whose capture variable is set to true are invoked, in tree order. Second, object’s own event listeners are invoked. And finally, and only if event’s bubbles attribute value is true, object’s ancestor event listeners are invoked again, but now in reverse tree order.

load isn’t a bubbling event, and – here’s the important part – it doesn’t target document. When you add a capture listener, you’re really getting load events from parts of the document’s content that normally receive the event, like scripts or images. On a page with only the script, you won’t see the listener get called at all:

<iframe srcdoc="<script>document.addEventListener('load', () => { alert('loaded'); }, true);</script>"></iframe>

Capturing and Bubbling using jQuery

jQuery only uses event bubbling. If you want to add an event handler that uses the capturing model, you have to do it explicitly using addEventListener, with the third argument true as you show in the question.

Event capturing and bubbling phases are REVERSED on the node that fires the event

https://www.w3.org/TR/DOM-Level-3-Events/#event-flow

Looking at the value of "eventPhase", you can see it is actually at the "target" phase. When at the target, it doesn't matter what you use for the capturing value, because it is neither capture nor bubbling phase. The handlers get called in the order that they are attached.

const input = document.querySelector('#btn');

//e.eventPhase: 1=Capture 2=Target 3=Bubbling
handler = (capture,e) => console.log(`eventPhase: ${e.eventPhase},`,`phase ${capture}`);
function handler1(e){console.log(`eventPhase: ${e.eventPhase},`,"Hi from handler1, phase: Bubbling")};
function handler2(e){console.log(`eventPhase: ${e.eventPhase},`,"Hi from handler2, phase: Capturing")};

input.addEventListener('click', handler.bind(null,'doesnt matter1'), !!Math.round(Math.random()));
input.addEventListener('click', handler1, false);
input.addEventListener('click', handler.bind(null,'doesnt matter2'), !!Math.round(Math.random()));
input.addEventListener('click', handler2, true);
input.addEventListener('click', handler.bind(null,'doesnt matter3'), !!Math.round(Math.random()));

document.body.addEventListener('click',handler.bind(null,'Bubbling'),false);
document.body.addEventListener('click',handler.bind(null,'Capturing'),true);
<input type="button" id="btn" value="I'm clickable button"/>

Examples of common, practical uses of event bubbling and capturing?

Practical event bubbling?

With or without jQuery (bearing in mind that you can handle bubbled events without using a library) there are a number of scenarios where you'd want to structure your code to take advantage of bubbling.

One example: let's say you have a page where elements are being created dynamically, but you want to process a click on those elements. You can't bind an event handler directly to them before they exist, but binding individual handlers to them when you create them is a bit of a pain. Instead, bind an event handler to a container object: click events will bubble up from the individual elements to the container but you can still tell which element was clicked - jQuery makes this easy if you are using the appropriate syntax of .on(), or .delegate() (or even .live() if you have a really old version of jQuery) because it sets this to the clicked element.

<div id="someContainer"></div>

$("#someContainer").on("click", ".dynamicElement", function() {
// this is the element, do something with it
});

This says that on click of an element with the class "dynamicElement" that is a child of the "someContainer" div do something. It will work regardless of whether the "dynamicElement" element existed at page load, was added later in response to some other user action, or perhaps loaded with Ajax.



Related Topics



Leave a reply



Submit