718 Part III ✦ Document Objects Reference Listing 29-2 (continued) NAME=”layerButton2” onClick=”alert(‘Event finally reached Button:’ + this.name)”></P> </FORM> </BODY> </LAYER> </BODY> </HTML> Passing events toward their targets If you capture a particular event type, your script may need to perform some lim- ited processing on that event before letting it reach its intended target. For exam- ple, perhaps you want to do something special if a user clicks an element with the Shift metakey pressed. In that case, the function that handles the event at the docu- ment level inspects the event’s modifiers property to determine if the Shift key was pressed at the time of the event. If the Shift key was not pressed, you want the event to continue on its way to the element that the user clicked. To let an event pass through the object hierarchy to its target, you use the routeEvent() method, passing as a parameter the event object being handled in the current function. A routeEvent() method does not guarantee that the event will reach its intended destination, because another object in between may have event capturing for that event type turned on and will intercept the event. That object, too, can let the event pass through with its own routeEvent() method. Listing 29-3 demonstrates event routing by adding onto the document being built in previous examples. While the clickable button objects are the same, additional powers are added to the document and layer function handlers that process events that come their way. For each of these event-capturing objects, you have additional checkbox settings to allow or disallow events from passing through after each level has processed them. The default settings for the checkboxes are like the ones in Listing 29-2, where event capture (for the click event) is set for both the document and layer objects. Clicking any button causes the document object’s event handler to process and none other. But if you then enable the checkbox that lets the event continue, you find that click events on the layer buttons cause alerts to display from both the document and layer object event handler functions. If you then also let events con- tinue from the layer object, a click on the button displays a third alert, showing that the event has reached the buttons. Because the main1 button is not in the layer, none of the layer object event handling settings affect its behavior. Listing 29-3: NN4 Capture, Release, and Route Events <HTML> <HEAD> <SCRIPT LANGUAGE=”JavaScript”> function setDocCapture(enable) { if (!enable) { document.captureEvents(Event.CLICK) 719 Chapter 29 ✦ Event Objects } else { document.releaseEvents(Event.CLICK) document.forms[0].setDocRte.checked = false docRoute = false } } function setLayerCapture(enable) { if (!enable) { document.layer1.captureEvents(Event.CLICK) } else { document.layer1.releaseEvents(Event.CLICK) document.forms[0].setLyrRte.checked = false layerRoute = false } } var docRoute = false var layerRoute = false function setDocRoute(enable) { docRoute = !enable } function setLayerRoute(enable) { layerRoute = !enable } function doMainClick(e) { if (e.target.type == “button”) { alert(“Captured in top document”) if (docRoute) { routeEvent(e) } } } document.captureEvents(Event.CLICK) document.onclick=doMainClick </SCRIPT> </HEAD> <BODY> <B>Capture, Release, and Routing of Event.CLICK</B> <HR> <FORM> <INPUT TYPE=”checkbox” NAME=”setDocCap” onMouseDown=”setDocCapture(this.checked)” CHECKED>Enable Document Capture <INPUT TYPE=”checkbox” NAME=”setDocRte” onMouseDown =”setDocRoute(this.checked)”>And let event continue<P> <INPUT TYPE=”checkbox” NAME=”setLyrCap” onMouseDown =”setLayerCapture(this.checked)” CHECKED>Enable Layer Capture <INPUT TYPE=”checkbox” NAME=”setLyrRte” onMouseDown =”setLayerRoute(this.checked)”>And let event continue <HR> <INPUT TYPE=”button” VALUE=”Button ‘main1’” NAME=”main1” onClick=”alert(‘Event finally reached Button:’ + this.name)”> </FORM> <LAYER ID=”layer1” LEFT=200 TOP=150 BGCOLOR=”coral”> Continued 720 Part III ✦ Document Objects Reference Listing 29-3 (continued) <HEAD> <SCRIPT LANGUAGE=”JavaScript”> function doLayerClick(e) { if (e.target.type == “button”) { alert(“Captured in layer1”) if (layerRoute) { routeEvent(e) } } } layer1.captureEvents(Event.CLICK) layer1.onclick=doLayerClick </SCRIPT> </HEAD> <BODY> <FORM> layer1<BR><P><INPUT TYPE=”button” VALUE=”Button ‘layerButton1’” NAME=”layerButton1” onClick=”alert(‘Event finally reached Button:’ + this.name)”></P> <P><INPUT TYPE=”button” VALUE=”Button ‘layerButton2’” NAME=”layerButton2” onClick=”alert(‘Event finally reached Button:’ + this.name)”></P> </FORM> </BODY> </LAYER> </BODY> </HTML> In some cases, your scripts need to know if an event that is passed onward by routeEvent() method activated a function that returns a value. This knowledge is especially valuable if your event must return a true or false value to let an object know if it should proceed with its default behavior (for example, whether a link should activate its HREF attribute URL or cancel after the event handler evaluates to return true or return false). When a function is invoked by the action of a routeEvent() method, the return value of the destination function is passed back to the routeEvent() method. That value, in turn, can be returned to the object that originally captured the event. Event traffic cop The last scenario is one in which a higher-level object captures an event and directs the event to a particular object elsewhere in the hierarchy. For example, you could have a document-level event handler function direct every click event whose modifiers property indicates that the Alt key was pressed to a Help button object whose own onClick event handler displays a help panel (perhaps shows an other- wise hidden layer). You can redirect an event to any object via the handleEvent() method. This method works differently from the others described in this chapter, because the object reference of this method is the reference of the object to handle the event 721 Chapter 29 ✦ Event Objects (with the event object being passed as a parameter, such as the other methods). As long as the target object has an event handler defined for that event, it will process the event as if it had received the event directly from the system (even though the event object’s target property may be some other object entirely). To demonstrate how this event redirection works, Listing 29-4 includes the final additions to the document being built so far in this chapter. The listing includes mechanisms that allow all click events to be sent directly to the second button in the layer ( layerButton2). The previous interaction with document and layer event capture and routing is still intact, although you cannot have event routing and redi- rection on at the same time. The best way to see event redirection at work is to enable both document and layer event capture (the default settings). When you click the main1 button, the event reaches only as far as the document-level capture handler. But if you then turn on the “Send event to ‘layerButton2’” checkbox associated with the document level, a click of the main1 button reaches both the document-level event handler and layerButton2, even though the main1 button is not anywhere near the layer button in the document object hierarchy. Click other checkboxes to work with the interaction of event capturing, routing, and redirection. Listing 29-4: NN4 Redirecting Events <HTML> <HEAD> <SCRIPT LANGUAGE=”JavaScript”> function setDocCapture(enable) { if (!enable) { document.captureEvents(Event.CLICK) } else { document.releaseEvents(Event.CLICK) document.forms[0].setDocRte.checked = false docRoute = false } } function setLayerCapture(enable) { if (!enable) { document.layer1.captureEvents(Event.CLICK) } else { document.layer1.releaseEvents(Event.CLICK) document.forms[0].setLyrRte.checked = false layerRoute = false } } var docRoute = false var layerRoute = false function setDocRoute(enable) { docRoute = !enable document.forms[0].setDocShortCircuit.checked = false docShortCircuit = false } Continued 722 Part III ✦ Document Objects Reference Listing 29-4 (continued) function setLayerRoute(enable) { layerRoute = !enable document.forms[0].setLyrShortCircuit.checked = false layerShortCircuit = false } var docShortCircuit = false var layerShortCircuit = false function setDocShortcut(enable) { docShortCircuit = !enable if (docShortCircuit) { document.forms[0].setDocRte.checked = false docRoute = false } } function setLayerShortcut(enable) { layerShortCircuit = !enable if (layerShortCircuit) { document.forms[0].setLyrRte.checked = false layerRoute = false } } function doMainClick(e) { if (e.target.type == “button”) { alert(“Captured in top document”) if (docRoute) { routeEvent(e) } else if (docShortCircuit) { document.layer1.document.forms[0].layerButton2.handleEvent(e) } } } document.captureEvents(Event.CLICK) document.onclick=doMainClick </SCRIPT> </HEAD> <BODY> <B>Redirecting Event.CLICK</B> <HR> <FORM> <INPUT TYPE=”checkbox” NAME=”setDocCap” onMouseDown=”setDocCapture(this.checked)” CHECKED>Enable Document Capture <INPUT TYPE=”checkbox” NAME=”setDocRte” onMouseDown =”setDocRoute(this.checked)”>And let event continue <INPUT TYPE=”checkbox” NAME=”setDocShortCircuit” onMouseDown =”setDocShortcut(this.checked)”>Send event to ‘layerButton2’<P> <INPUT TYPE=”checkbox” NAME=”setLyrCap” onMouseDown =”setLayerCapture(this.checked)” CHECKED>Enable Layer Capture <INPUT TYPE=”checkbox” NAME=”setLyrRte” onMouseDown =”setLayerRoute(this.checked)”>And let event continue 723 Chapter 29 ✦ Event Objects <INPUT TYPE=”checkbox” NAME=”setLyrShortCircuit” onMouseDown =”setLayerShortcut(this.checked)”>Send event to ‘layerButton2’<P> <HR> <INPUT TYPE=”button” VALUE=”Button ‘main1’” NAME=”main1” onClick=”alert(‘Event finally reached Button:’ + this.name)”> </FORM> <LAYER ID=”layer1” LEFT=200 TOP=200 BGCOLOR=”coral”> <HEAD> <SCRIPT LANGUAGE=”JavaScript”> function doLayerClick(e) { if (e.target.type == “button”) { alert(“Captured in layer1”) if (layerRoute) { routeEvent(e) } else if (layerShortCircuit) { document.forms[0].layerButton2.handleEvent(e) } } } layer1.captureEvents(Event.CLICK) layer1.onclick=doLayerClick </SCRIPT> </HEAD> <BODY> <FORM> layer1<BR><P><INPUT TYPE=”button” VALUE=”Button ‘layerButton1’” NAME=”layerButton1” onClick=”alert(‘Event finally reached Button:’ + this.name)”></P> <P><INPUT TYPE=”button” VALUE=”Button ‘layerButton2’” NAME=”layerButton2” onClick=”alert(‘Event finally reached Button:’ + this.name)”></P> </FORM> </BODY> </LAYER> </BODY> </HTML> IE4+ event propagation Event propagation in IE4+ flows in the opposite direction of the NN4 event cap- ture model. IE’s model is called event bubbling, in which events “bubble” upward from the target object through the element containment hierarchy. It’s important to distinguish between the old-fashioned document object hierarchy (followed in the NN4 event capture model) and the more modern notion of HTML element contain- ment — a concept that carries to the W3C DOM as well. A good way to demonstrate the effect of event bubbling — a behavior that is turned on by default — is to populate a simple document with lots of event handlers to see which ones fire and in what order. Listing 29-5 has onClick event handlers defined for a button inside a form, the form itself, and other elements and object all the way up the hierarchy out to the window. 724 Part III ✦ Document Objects Reference Listing 29-5: Event Bubbling Demonstration <HTML onClick=”alert(‘Event is now at the HTML element.’)”> <HEAD> <TITLE>Event Bubbles</TITLE> <SCRIPT LANGUAGE=”JavaScript”> function init() { window.onclick = winEvent document.onclick = docEvent document.body.onclick = docBodEvent } function winEvent() { alert(“Event is now at the window object level.”) } function docEvent() { alert(“Event is now at the document object level.”) } function docBodEvent() { alert(“Event is now at the BODY element.”) } </SCRIPT> </HEAD> <BODY onLoad=”init()”> <H1>Event Bubbles</H1> <HR> <FORM onClick=”alert(‘Event is now at the FORM element.’)”> <INPUT TYPE=”button” VALUE=”Button ‘main1’” NAME=”main1” onClick=”alert(‘Event started at Button: ‘ + this.name)”> </FORM> </BODY> </HTML> You can try this listing in IE4+ and even NN6, because the latter observes event bubbling. But you will notice differences in the precise propagation among IE4+/Windows, IE4+/Macintosh, and NN6. But first, notice that after you click the button in Listing 29-5, the event first fires at the target: the button. Then the event bubbles upward through the HTML containment to fire at the enclosing FORM ele- ment; next to the enclosing BODY element; and so on. Where the differences occur are after the BODY element. Table 29-1 shows the objects for which event handlers are defined in Listing 29-5 and which objects have the click event bubble to them in the three classes of browsers. Table 29-1 Event Bubbling Variations for Listing 29-5 Event Handler Location IE4+/Windows IE4+/Macintosh NN6 BUTTON yes yes yes FORM yes yes yes 725 Chapter 29 ✦ Event Objects Event Handler Location IE4+/Windows IE4+/Macintosh NN6 BODY yes yes yes HTML yes no yes document yes yes yes window no no yes Despite the discrepancies in Table 29-1, events do bubble through the most likely HTML containers that come to mind. The object level with the most global scope and that works in all browser categories shown in the table is the document object. Preventing IE event bubbling Because bubbling occurs by default, there are times when you may prefer to pre- vent an event from bubbling up the hierarchy. For example, if you have one handler at the document level whose job is to deal with the click event from a related series of buttons, any other object that receives click events will allow those events to bubble upward to the document level unless the bubbling is cancelled. Having the event bubble up could conflict with the document-level event handler. Each event object in IE has a property called cancelBubble. The default value of this property is false, which means that the event bubbles to the next outer- most container that has an event handler for that event. But if, in the execution of an event handler, that property is set to true, the processing of that handler fin- ishes its job, but the event does not bubble up any higher. Therefore, to stop an event from bubbling beyond the current event handler, include the following state- ment somewhere in the handler function: event.cancelBubble = true You can prove this to yourself by modifying the page in Listing 29-5 to cancel bubbling at any level. For example, if you change the event handler of the FORM ele- ment to include a statement that cancels bubbling, the event goes not further than the FORM in IE (the syntax is different for NN6, as discussed later): <FORM onClick=”alert(‘Event is now at the FORM element.’); event.cancelBubble=true”> Redirecting events Starting with IE5.5, you can redirect an event to another element, but with some limitations. The mechanism that makes this possible is the fireEvent() method of all HTML element objects (see Chapter 15). This method isn’t so much redirecting an event as causing a brand-new event to be fired. But you can pass most of the properties of the original event object with the new event by specifying a reference to the old event object as the optional second parameter to the fireEvent() method. The big limitation in this technique, however, is that the reference to the target element gets lost in this hand-off to the new event. The srcElement property of the old event gets overwritten with a reference to the object that is the target of the call to fireEvent(). For example, consider the following onClick event handler func- tion for a button inside a FORM element: 726 Part III ✦ Document Objects Reference function buttonEvent() { event.cancelBubble = true document.body.fireEvent(“onclick”, event) } By cancelling event bubbling, the event does not propagate upward to the enclosing FORM element. Instead, the event is explicitly redirected to the BODY ele- ment, passing the current event object as the second parameter. When the event handler function for the BODY element runs, its event object has information about the original event, such as the mouse button used for the click and the coor- dinates. But the event.srcElement property points to the document.body object. As the event bubbles upward from the BODY element, the srcElement property continues to point to the document.body object. You can see this at work in Listing 29-6 for IE5.5+. Listing 29-6: Cancelling and Redirecting Events in IE5.5+ <HTML onClick=”revealEvent(‘HTML’, event)”> <HEAD> <TITLE>Event Cancelling & Redirecting</TITLE> <SCRIPT LANGUAGE=”JavaScript”> // display alert with event object info function revealEvent(elem, evt) { var msg = “Event (from “ + evt.srcElement.tagName + “ at “ msg += event.clientX + “,” + event.clientY + “) is now at the “ msg += elem + “ element.” alert(msg) } function init() { document.onclick = docEvent document.body.onclick = docBodEvent } function docEvent() { revealEvent(“document”, event) } function docBodEvent() { revealEvent(“BODY”, event) } function buttonEvent(form) { revealEvent(“BUTTON”, event) // cancel if checked (IE4+) event.cancelBubble = form.bubbleCancelState.checked // redirect if checked (IE5.5+) if (form.redirect.checked) { document.body.fireEvent(“onclick”, event) } } </SCRIPT> </HEAD> <BODY onLoad=”init()”> <H1>Event Cancelling & Redirecting</H1> <HR> <FORM onClick=”revealEvent(‘FORM’, event)”> 727 Chapter 29 ✦ Event Objects <P><BUTTON NAME=”main1” onClick=”buttonEvent(this.form)”> Button ‘main1’ </BUTTON></P> <P><INPUT TYPE=”checkbox” NAME=”bubbleCancelState” onClick=”event.cancelBubble=true”>Cancel Bubbling at BUTTON<BR> <INPUT TYPE=”checkbox” NAME=”redirect” onClick=”event.cancelBubble=true”> Redirect Event to BODY</P> </FORM> </BODY> </HTML> Listing 29-6 is a modified version of Listing 29-5. Major additions are enhanced event handlers at each level so that you can see the tag name of the event that is regarded as the srcElement of the event as well as the coordinates of the click event. With both checkboxes unchecked, events bubble upward from the button, and the BUTTON element is then shown to be the original target all the way up the bubble hierarchy. If you check the Cancel Bubbling checkbox, the event goes no fur- ther than the BUTTON element, because that’s where event bubbling is turned off. If you then check the Redirect Event to BODY checkbox, the original event is can- celled at the BUTTON level, but a new event is fired at the BODY element. But notice that by passing the old event object as the second parameter, the click loca- tion properties of the old event are applied to the new event directed at the BODY. This event then continues to bubble upward from the BODY. As a side note, if you uncheck the Cancel Bubbling checkbox but leave the Redirect Event box checked, you can see how the redirection is observed at the end of the BUTTON’s event handler, and something special goes on. The original event is held aside by the browser while the redirected event bubbles upward. As soon as that event processing branch finishes, the original bubbling propagation carries on with the FORM. Notice, though that the event object still knows that it was tar- geted at the BUTTON element, and the other properties are intact. This means that for a time, two event objects were in the browser’s memory, but only one is “active” at a time. While the redirected event is propagating, the window.event object refers to that event object only. NN6+ event propagation Yielding to arguments in favor of both event capture and event bubbling, the W3C DOM group managed to assemble an event model that employs both propaga- tion systems. Although forced to use new syntax so as not to conflict with older browsers, the W3C DOM propagation model works like the NN4 one for capture and like IE4+ for bubbling. In other words, an event bubbles by default, but you can also turn on event capture if you want. Thus, an event first trickles down the element containment hierarchy to the target; then it bubbles up through the reverse path. Event bubbling is on by default, just as in IE4+. To enable capture, you must apply a W3C DOM event listener to an object at some higher container. Use the addEventListener() method (see Chapter 15) for any visible HTML element or node. One of the parameters of the addEventListener() method determines whether the event listener function should be triggered while the event is bubbling or is captured. . LEFT=200 TOP=150 BGCOLOR=”coral”> Continued 720 Part III ✦ Document Objects Reference Listing 29-3 (continued) <HEAD> <SCRIPT LANGUAGE= JavaScript > function doLayerClick(e) { if (e.target.type. this.name)”></P> </FORM> </BODY> </LAYER> </BODY> </HTML> Passing events toward their targets If you capture a particular event type, your script may need to perform some lim- ited processing on that event before. behavior. Listing 29-3: NN4 Capture, Release, and Route Events <HTML> <HEAD> <SCRIPT LANGUAGE= JavaScript > function setDocCapture(enable) { if (!enable) { document.captureEvents(Event.CLICK) 719 Chapter