Dynamic HTML: The Definitive Reference, 2rd Ed.Dynamic HTML: The Definitive ReferenceSearch this book

6.5. Event Propagation

In some DHTML applications, it is not efficient to have target elements process events. For example, if you have a page that allows users to select and drag elements around the page, it is quite possible that one set of centralized functions can handle that operation for all elements. Rather than define event handlers for all of those elements, it is better to have the mouse-related events go directly to an object or element that has scope over all the draggable elements. In other words, one event handler can do the job of a dozen. For this kind of treatment to work, events must be able to propagate through the hierarchy of objects or nodes in the document. IE 5 and later and the W3C (Netscape 6) event models share some, but not all, event propagation schemes. For the most typical applications, you can easily equalize the small differences in implementation details and syntax you use to override the natural flow.

W3C DOM event propagation in Netscape 6 and later can be summarized thus: in response to a user or system action, an event starts at the outermost container and follows the most direct route ("trickles down") through the node container hierarchy to the intended target; after it reaches its target, the event reverses course and "bubbles upward" through the same node hierarchy back to the top, from which it disappears. The trickle-down portion of the journey is called the capture phase, while the return trip is called the bubbling phase. The IE propagation model consists only of the bubbling phase. While IE 5 and later has an event feature related to capture (described later in this chapter), its operation is not along the lines of the W3C capture phase of propagation.

Consider the following skeletal structure of an HTML document:

<html>
<body>
    <form>
        <div id="div1">
            <input id="txt1" type="text">
        </div>
        <div id="div2">
            <input id="txt2" type="text">
        </div>
    </form>
</body>
</html>

As the user types into the txt2 text input field, an onkeypress event begins its journey at an outermost container in Netscape 6, works its way through containers on its way to the text box (where IE's event starts), and then goes back to the outermost container. The precise top-level container varies with browser version. For Netscape 6, the window object is the master container for event propagation purposes; IE holds the line at the document node. Figure 6-1 depicts the onkeypress event propagation sequence through the objects of this document for three different browser versions.

Figure 6-1

Figure 6-1. Sample event propagation sequences

You can assign an onkeypress event handler for any and all of the nodes in the hierarchy to process the event. By default, a Netscape 6 event handler "listens" for events only during the bubbling phase, which means that Netscape 6 behaves like IE. Event bubbling isn't as anarchic as it sounds. In fact, it's quite flexible if you're careful to avoid conflicts that may occur at higher containment levels.

6.5.1. Event Bubbling

Event bubbling is the default propagation path for most events starting in IE 4 and Netscape 6. Some system-fired events work only in their target elements. For example, if you have an onload event listener assigned to a few img elements and the body element, you probably don't want an img element's load event to bubble up to the body element, firing each time an image's src property changes.

Event bubbling is often vital in scripting events for elements that display body text. The node-centric W3C DOM allows ordinary text nodes to be event listeners. Therefore, if you assign a mouse-related event to a text container, the target node of the event will be the text node within that container. By default the event bubbles outward to the container where the event can be easily processed, but the event object's properties point to the text node, not the container. This is different from the IE event model, in which only elements are targets of events. To equalize this possibility in processing an event, your scripts must take the node into account. Here is one cross-browser way to make sure that your function locates a reference to the element surrounding the text (or, as a last resort, the document node):

function myFunction(evt) {
    evt = (evt) ? evt : ((event) ? event : null);
    if (evt) {
        var elem = (evt.target) ? evt.target : 
                   ((evt.srcElement) ? evt.srcElement : null);
        if (elem) {
            elem = (elem.nodeType == 1 || elem.nodeType == 9) ? elem : 
                   elem.parentNode;
            // ok, now we're ready to work with the element
        }
    }
}

Most events triggered by user action with the mouse and keyboard bubble upward through the hierarchy from the target element. Conflicts can arise, however. For example: you assign an onmousedown event handler for several images so they can swap .jpg files while the mouse is being held down. But you also define an onmousedown event handler for the body element to act as a single handler to assist in dragging several positioned div elements around the page. To prevent the img element mousedown events from bubbling up through the hierarchy, you can explicitly instruct an event not to bubble beyond a specific element.

The cross-browser way of canceling event bubbling (starting in IE 4 and Netscape 6) involves the Boolean cancelBubble property of the event object, adjusted within the event handler function. It's the same property name and behavior for both IE and Netscape 6 event objects (this property is not a member of the W3C DOM event object, but Netscape 6 implements it as a compatibility convenience). The default value for this property is false, meaning that event bubbling takes place. But if you set this property to true, the event does not bubble past the current event handler.

Set the cancelBubble property to false in a script statement executing in the current event's function. Thus, if you assign an event handler as an element property, the bubble cancelation can take place in the function invoked by the event handler:

function myFunction(evt) {
    evt = (evt) ? evt : ((event) ? event : null);
    if (evt) {
        // include next statement anywhere in this block
        evt.cancelBubble = true;
    }
}

Only one event bubbles at any given instant, so this statement knows to cancel the right one. It also means that you can let an event bubble part of the way through the element hierarchy, but stop it at any desired element, so as not to interfere with other elements higher up the chain.

6.5.2. W3C Event Capture

Although event bubbling is the default mechanism in modern browsers, a propagating event in Netscape 6 or later starts its life by trickling down to the target. If you place an event listener for the event's type at a higher level, however, it will ignore the event as it trickles down unless event capture for that element and that event type is turned on.

To engage event capture, use the same addEventListener( ) method that the W3C event model prefers for event binding. The third parameter is a Boolean value that controls event capture. When you set the parameter to true, the element invokes the listener function during capture phase; after that function completes its task, the event continues its journey toward to the target element. Therefore, it is perfectly "legal" to add two separate event listeners to an element for the same event type. In capture phase, one listener function runs; in bubbling phase, another listener function runs.

Using the same three parameters, you can eliminate the event listener for the desired propagation phase with the removeEventListener( ) method. Thus, you could temporarily engage capture-phase processing and remove it without disturbing bubbling-phase event processing for the same element and event type.

At any point along W3C event propagation, you can prevent the event from going any further by invoking the event object's stopPropagation( ) method in a statement inside the listener function. Netscape 6 or later also wires the convenience cancelBubble property to work during capture phase as well.

The W3C root event object (and therefore event objects of all types) implements a handful of other properties that may be helpful while processing events within the two-way propagation model. Table 6-4 lists these properties, for which IE (through Version 6 on Windows) provides no analogues. A shared event listener function might use these (and other properties) to build code branches that execute when the desired combination of conditions exist (e.g., when the target's class name is "foo" and the event is being processed from a container of several elements of the same class).

Table 6-4. W3C event object propagation properties

Property

Description

bubbles

Boolean true if event can bubble

currentTarget

Reference to the node whose event listener invoked the current listener function

eventPhase

Integer indicating in which phase the event listener is processing (1 is capture; 2 is at target; 3 is bubbling)

6.5.3. IE/Windows Event Capture

Microsoft's view of event capture is quite different from the W3C view. IE 5 and later event capture operates only with mouse events. In fact, when you invoke an element object's setCapture( ) method, you instruct the browser to direct all mouse events on the page to that element rather than to their targets. Events bubble up from the capturing element, unless canceled.

This event mechanism is intended primarily for temporary activation within a page. For example, the body element can contain an oncontextmenu event handler that waits for a Windows user to click the right (nondominant) mouse button. You can take this opportunity not only to block the display of the browser's own context menu (by setting event.returnValue to false), but also to display your own menu composed of DHTML positioned elements. While the custom menu is visible, you want all mouse events to head for the menu so that nothing else on the page is accessible via the mouse until either a choice is made from the menu or the right mouse button is clicked again. Either action hides the custom context menu and invokes releaseCapture( ) to allow mouse events to reach their normal targets again.

It's not uncommon for IE/Windows to implement proprietary DOM features that allow web applications to mimic operating-system-specific behaviors. In an intranet development environment targeting IE/Windows only, this tactic makes perfect sense. But such tight integration reduces the likelihood that these features will become part of an operating-system-agnostic W3C recommendation. It also makes it more difficult for Microsoft to implement some W3C recommendations that conflict with existing mechanisms.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.