Oooo Bubbles!

30 April 2011

I probably watch too many kids movies. I am, however trying to refer to Event Bubbling and Capture and how it all works. So without further ado about nothing…

The code

This is the code we’ll be playing with for the rest of this article. A typical navigation structure (without anchors, because my blog wont allow me to add them).

<li class="hover">
    <span>Hover over me</span>
    <ul>
        <li>sub menu 1</li>
        <li>sub menu 2</li>
    </ul>
</li>
var li = document.getElementsByClassName('hover')[0],
    span = li.getElementsByTagName('span')[0];

What target?

An event listener will supply an object as an argument which contains all the event information. Surprisingly, each browser took a different route on this, so all the event properties are not the same or all available across the browsers. The most obvious one being the target of the event. So we try for e.target first, and fall back to e.srcElement if it doesn’t exist.

function hovered(e) {
    var target = e.target || e.srcElement;
}

For simplicity sake, I’m going to refer to the target as target, and not srcElement. I’m also only going to use addEventListener, and not resort using attachEvent as well.

What element stack?

Basically, the element stack is the ancestors of the element in question. For the above code, the element stack of li “sub menu 1” is ul, li.hover, etc.

Bubbling over capture

Surprisingly, again, Microsoft and Netscape had different opinions on how to deal with event orders. Something I think the peeps over at Microsoft got right. From the above code, we would like a sub ul to appear when you mouse over the li.hover. What happens when the user mouses over the span? In both instances, the browser registers an event has occurred and checks for any listeners within the element stack.

Capture

In the capture model, the browser checks from the top of the element stack down firing all other event listeners it finds along the way till it hits the span.

li.addEventListener('mouseover', hovered, true); //true denotes capture model

Bubble

In the bubble model, the browser starts at the span, and fires all the event listeners up the element stack.

li.addEventListener('mouseover, hovered, false); //false denotes bubble model

So what does this all mean

Capturing and bubbling don’t matter when you’ve only got one event of that type in the element stack, but when there are multiple events of the same type it begins to make a difference. Based on the same event as above (user mouses over the span), however we’ve added another mouse over event listener on the span.

In the capture model the li event would fire first, then the span event

li.addEventListener('mouseover', hovered, true); 
span.addEventListener('mouseover', hovered, true);

In the bubble model the span event would fire first, then the li event

li.addEventListener('mouseover', hovered, false); 
span.addEventListener('mouseover', hovered, false);

I think the latter makes more sense, as, in the html element stack, the span would always sit on top of the li, so that is the order to which the events should fire.

Which element fired that event listener?

This did.

Say in the above example, you have two event listeners using the hovered function, and you would like to know to which element fired it. Basic javascript would prevail to say that as the listener is attached to the element, the scope should be set to that listener. Sadly Microsoft didn’t believe that knowing which element fired the event was important. In all compliant browsers, you can use this to resolve the owner.

function hovered(e) {
    var target = e.target || e.srcElement,
        owner = this;
}
li.addEventListener('mouseover', hovered, true); 
span.addEventListener('mouseover', hovered, true);

If span is clicked; hovered would then fire for span.addEventListener where owner would be span; hovered would then fire for li.addEventListener where owner would be li. In IE owner would be window for both listeners, not so helpful!

Most Important things to note

As much as it may sounds like it, each listener is NEVER fired more than once per event. Bubbling and capturing are related to what happens when an event occurs as it checks for listeners within the element stack. The target of the event is always the same. Considering Internet Explorer doesn’t support the capture model and it holds little advantage over the bubble model, it’s easily avoided. And finally, this is not always as it seems.

Matt Sain

Ramblings of a developer, designer and child subscribe

This blog is a public GitHub repository. If you find an error I will not be surprised... but if you fork and edit the blog and send me a pull request you'd be pretty awesome in my book.

Featured Repos