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 documentand layer object event han
Trang 1Listing 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 clickevent) is set for both the documentand layer
objects Clicking any button causes the documentobject’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
documentand 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 main1button 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)
Trang 2document.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
Trang 3Listing 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 trueor falsevalue to let an object know if it should proceed with its default behavior (for example, whether a link should activate its HREFattribute URL or cancel after the event handler evaluates
to return trueor 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
modifiersproperty indicates that the Alt key was pressed to a Help button object whose own onClickevent 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
Trang 4the event as if it had received the event directly from the system (even though the
event object’s targetproperty 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 clickevents 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 main1button, 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 main1button reaches both the document-level event handler
and layerButton2, even though the main1button 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
Trang 5Listing 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
Trang 6<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 onClickevent 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
Trang 7Listing 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 eleele-ment; 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 clickevent bubble to them
in the three classes of browsers
Table 29-1 Event Bubbling Variations for Listing 29-5
Trang 8BODY yes yes 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 epre-vent from bubbling up the hierarchy For example, if you have one handler
at the documentlevel whose job is to deal with the clickevent from a related
series of buttons, any other object that receives clickevents will allow those
events to bubble upward to the documentlevel unless the bubbling is cancelled
Having the event bubble up could conflict with the document-level event handler
Each eventobject 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 stateele-ment 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 eventobject with the new event by specifying a reference
to the old eventobject 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 srcElementproperty 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 onClickevent handler
func-tion for a button inside a FORM element:
Trang 9function 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 eventobject as the second parameter When the event handler function for the BODY element runs, its eventobject has information about the original event, such as the mouse button used for the click and the coor-dinates But the event.srcElementproperty points to the document.bodyobject
As the event bubbles upward from the BODY element, the srcElementproperty continues to point to the document.bodyobject 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)”>
Trang 10<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 srcElementof 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 eventobject 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 eventobject still knows that it was
tar-geted at the BUTTON element, and the other properties are intact This means that
for a time, two eventobjects 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 eventobject 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