HTML Dom: Which Events Do Not Bubble

HTML DOM: Which events do not bubble?

Any events specific to one element do not bubble: focus, blur, load, unload, change, reset, scroll, most of the DOM events (DOMFocusIn, DOMFocusOut, DOMNodeRemoved, etc), mouseenter, mouseleave, etc

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>

Javascript Events, Capturing works Bubbling doesn't

dispatchEvent dispatches your event. Summarizing, that means

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.

Therefore, if the bubbles attribute is not true, the event won't bubble.

You can set the bubbles attribute to true when you create the event with Event:

event = new Event(type [, eventInitDict])

Returns a new event whose type attribute value is set to
type. The optional eventInitDict argument allows for setting the bubbles and cancelable attributes via object members
of the same name.

event not bubbling like I think it should

I discovered that if I add the same listener to every element in a segment of the DOM tree a single mouse click will trigger a click event for each element starting with the element clicked, traveling up each successive element (bubbling). I can then test each node for className "item" and when found read its id. This produces the result I want, but means hundreds of elements are assigned the same listener. It's probably just hundreds of pointers, but doesn't strike me as very efficient

However if I add the listener to just the top element a click event only fires the listener once no matter which element actually received the click (e.g. the img but not the td it's in, or the tr the td is in, etc.). I see lots of reasons this is a good thing under normal circumstances, but it doesn't do what I need.

Thinking about it I came up with the following solution. I add the listener to the top element. Then the handler tests if e.target has a className of "item", if not it walks up each successive parent until it does find the node with a className "item", then reads the id of that node (which is the inventory item number I am looking for).

function doSomething(e) {   if(e.target !== e.currentTarget){        var el =  e.target;        do {          el = el.parentNode;        } while (el.className !="item");
var clickedItem = el.id; console.log("Clicked item id: " + clickedItem); }}
topofTree= document.querySelector('#container') ;topofTree.addEventListener("click",doSomething) ;
<div id="container">  <table class="item" id="0064">    <tr>      <td><img src="/spiritmasks/images/MSK_spirit_mask_8.jpg" ></td>      <td>        <table>          <tr><td>Item ID:</td><td>0064</td></tr>          <tr><td>Display Name:</td><td>Spirit Mask #8</td></tr>          <tr><td>Description:</td><td></td></tr>          <tr><td>Type:</td><td>SPIRIT MASK</td></tr>          <tr><td>Location:</td><td>GALLERY</td></tr>          <tr><td>In Slide Show:</td><td>NO</td></tr>          <tr><td>Size:</td><td></td></tr>          <tr><td>Price:</td><td></td></tr>          <tr><td>Comments:</td><td></td></tr>        </table>      </td>    </tr>  </table></div>

Why do events bubble up the dom?

Your statement is wrong. The default for simple events is not to bubble:

Firing a simple event named e means that a trusted event with
the name e, which does not bubble (except where otherwise
stated)
and is not cancelable (except where otherwise stated), and
which uses the Event interface, must be created and
dispatched at the given target.

So the HTML related events which bubble it's because the spec explicitly says so, presumably because it makes more sense this way.

Some events which bubble:

  • Click events:

    Firing a click event means firing a synthetic mouse event named click, which bubbles and is cancelable.

  • Unfocusing steps:

    fire a simple event that bubbles named change at the element.

Some events which do not bubble:

  • Error events when updating image data

    fire a simple event named error at the img element

  • Readystatechange events when changing the current document readiness

    fire a simple event named readystatechange at the Document object.

bubble up events and html/dom structure?

does the html "tree" is arranged on the opposite direction (flip vertical)? because I see a lot of examples that shows a tower being built from bottom to top

It's a DOM tree, not a DOM tower. In computer science the convention is to draw trees with their root at the top and their leafs at the bottom.

The terms "up" and "down" make more sense if you think of the DOM as nested (which it indeed is) instead of just being connected in some direction. Then you will dive down into the more detailed structures, and bubble back up to the outermost layers.

For a definitive answer, read the Event dispatch and DOM event flow chapter in the W3 specification.

Why don't audio and video events bubble?

The reason why event bubbling exists is solve the ambiguous question of which element is the intended target of the event. So, if you click on a div, did you mean to click the div, or its parent? If the child doesn't have a click handler attached, then it checks the parent, and so on. I'm sure you know how that works.

The reason why audio events don't bubble is because they don't make sense on any other element. There's no ambiguity when you trigger a timeupdate on an audio element whether it's meant for the audio element itself or its parent div, so there's no need to bubble it.

You can read a fuller history of event bubbling here

Event delegation

Event delegation is still possible by utilizing the capturing phase of the event. Simply add true as the third argument for addEventListener which looks like this:

document.addEventListener('play', function(e){
//e.target: audio/video element
}, true);

Note, that this event doesn't bubble, but goes down the DOM-tree and can't be stopped with stopPropagation.

In case you want to use this with the jQuery's .on/.off methods (for example to have namespacing and other jQuery event extensions). The following function, taken form the webshim library, should become usefull:

$.createEventCapturing = (function () {
var special = $.event.special;
return function (names) {
if (!document.addEventListener) {
return;
}
if (typeof names == 'string') {
names = [names];
}
$.each(names, function (i, name) {
var handler = function (e) {
e = $.event.fix(e);

return $.event.dispatch.call(this, e);
};
special[name] = special[name] || {};
if (special[name].setup || special[name].teardown) {
return;
}
$.extend(special[name], {
setup: function () {
this.addEventListener(name, handler, true);
},
teardown: function () {
this.removeEventListener(name, handler, true);
}
});
});
};
})();

Usage:

$.createEventCapturing(['play', 'pause']);

$(document).on('play', function(e){
$('audio, video').not(e.target).each(function(){
this.pause();
});
});


Related Topics



Leave a reply



Submit