Section D. Event bubbling and capturing


D. Event bubbling and capturing

Suppose you have a <div> that contains a <form> that contains a form field. All three elements have an onclick event handler. The user clicks on the form field. Obviously, the event handler of the form field should now fire. But what about the <div>'s and the <form>'s event handlers? Should they fire too?

Figure 7.2. div, form, and input all have an onclick event handler. The user clicks on the input.


The answer is Yes. If an event takes place, and several event handlers are eligible to handle the event, all of them fire.

But which event handler fires first? The <div>'s, because it's the uppermost element? Or the form field's, because it's the element the user clicked on? The answer depends on whether you use event capturing or event bubbling.

Event bubbling means that the event starts on its target (the element the user clicked on, i.e., the form field), fires the target's onclick event handler, and then bubbles up through the document tree from ancestor to ancestor until it reaches the document element. For any new HTML element it encounters, it also fires any relevant event handlers, if present, and therefore the onclick event handlers of the <form> and the <div> also fire, in that order.

Figure 7.3. Event bubbling. The events fire in the order form field, form, div.


Event capturing is exactly the opposite. It starts at the document level and travels down the document tree to the event target (the form field). For each HTML element it encounters, it fires any relevant event handlers. Therefore, the handlers fire in the order <div>, <form>, form field.

Figure 7.4. Event capturing. The events fire in the order div, form, form field.


W3C model

In the W3C event model, both capturing and bubbling occur. When an event fires, it is first captured by the document, and then travels down the document tree to the event target. This is called the capturing phase. Once the event arrives at its target, the bubbling phase starts, during which the event travels back upwards to the document.

Figure 7.5. In the W3C event model, event capturing takes place first, followed by event bubbling.


However, an event handler is always set for only one of these phases. As we saw in 7C, addEventListener() and removeEventListener() need a boolean argument true or false. true means that the event is set for the capturing phase, and false means the event is set for the bubbling phase.

So suppose we do this:

var field = [the form field]; var div = [the div]; field.addEventListener('click',doThis,false); div.addEventListener('click',doThat,false); 


When the user clicks on the form field, the event starts at the document level and travels downwards to the form field. During that trip down, it does not encounter any event set for the capturing phase, so the first event handler that fires is the form field's own doThis(). Then the event enters the bubbling phase and travels back to the document. Now it encounters an event set for the bubbling phase, and the <div>'s doThat() event handler is fired.

Let's change the code:

div.addEventListener('click',doThat,true); 


Now if the user clicks on the form field, the event starts at the document level and travels downwards to the form field. During that trip it encounters an event set for the capturing phase, and doThat() is executed. Next it travels onwards to the event target and fires the form field's own doThis(). Then the event enters the bubbling phase and travels back to the document. It encounters no events set for the bubbling phase, so no event handlers are fired.

Traditional and Microsoft models

The traditional and Microsoft models support only bubbling, not capturing. In order to avoid browser incompatibilities, it's therefore best to restrict yourself to event bubbling. Besides, in most practical cases, there is no real difference between capturing and bubbling.

The Microsoft model allows you to capture mouse events through the setCapture() and releaseCapture() methods, but this feature is hardly ever used.

Practical uses of event bubbling

Event bubbling allows you to use one event handler to service many HTML elements. See how it's being done in Dropdown Menu:

<ul > <li><a href="#">News</a>   <ul>     <li><a href="#">Press Releases</a>            <ul>                   <li><a href="#">Release 1</a></li>                   <li><a href="#">Release 2</a></li>                   <li><a href="#">Release 3</a></li>            </ul>    </li>    etc. </ul> 


[Dropdown Menu, lines 7-11]

var lists = document.getElementsByTagName('ul'); for (var i=0;i<lists.length;i++) {     if (lists[i].className != 'menutree') continue;     lists[i].onmouseover = navMouseOver;     lists[i].onmouseout = navMouseOut; 


When the menu is initialized, the script goes through all <ul>s in the document and sees if they have a class 'menutree'. If one does, the script sets onmouseover and onmouseout event handlers on this <ul>.

However, the actual events take place on the <a> elements that form the visible part of the dropdown menu. These elements themselves do not have event handlers. That's no problem, because the events bubble up and eventually reach the <ul>. Once they arrive there, the navMouseOver() or navMouseOut() event handler is fired, and the dropdown menu works.

Effectively, this approach allows you to concentrate events on a lot of elements in one spot. You have to set only one single event handler on one single HTML element, and then you calmly wait until the events that take place on its descendants bubble up to that element. This keeps your script administration simple.

Usable Forms uses the same trick, by the way: it defines one general onclick event handler on the document. Event bubbling makes sure that every click event in the entire document eventually reaches this handler.

[Usable Forms, line 53]

addEventSimple(document,'click',showHideFields); 


Capturing Instead of Bubbling

This one-handler-for-all-elements trick would also work fine with event capturing. The click event encounters the handlers on the <ul> in the capturing phase, fires them, moves on to the target and then back in the bubbling phase, during which it encounters no more event handlers. The net result is exactly the same.


Interface events: no bubbling

You cannot use event bubbling in all situations, because some events are not valid on some HTML elements. The most restrictive rule is that interface events such as change or submit don't work on the document or window.

Take Textarea Maxlength. Theoretically it could service an unlimited number of textareas. Therefore, setting one general event handler on the document, and waiting for the events to bubble up would seem a good idea.

Unfortunately, the change event is not allowed on the document so the change events that are initiated on textareas would be unnoticed by the document, and my script would fail:

   document.onchange = checkMaxLength; // event never fires 


Therefore I'm forced to define an onchange event handler on every single textarea.

As a general rule, you can catch mouse and keyboard events on the document level, but not interface events. However, you should always test this general rule for your specific events and browsersyou might get lucky.

Cancelling event propagation

The W3C and Microsoft models allow you to cancel event capturing and bubbling. You are allowed to say, "This event should not propagate further." The traditional model does not offer a similar feature.

Obviously, the two models each need their own command. In the W3C model you should call the stopPropagation() method of the event object, while the Microsoft model expects you to set the cancelBubble property to true. (We already discussed this bit of code in 5J.)

var evt = [the event object]; if (evt.stopPropagation)     evt.stopPropagation(); evt.cancelBubble = true; 


To avoid odd side-effects in complicated DHTML menus, it is occasionally useful to cancel the propagation of the mouseover and mouseout events after they've been handled. Dropdown Menu is too simple to need such drastic measures, but sometimes clients ask for vastly more complicated structures, and in those cases this trick might save your bacon. Or not.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net