For the form object, however, the capture-type event listener is directed to one function, while a bubble-type listener for the same object is directed at a separate function.. Listing 2
Trang 1Listing 29-7 is a simplified example that demonstrates how a click event aimed at
a button can be both captured and allowed to bubble Most event handling func-tions are assigned inside the init()function Borrowing code from Listing 29-5, event handlers are assigned to the window, document, and BODY objects as prop-erty assignments These are automatically treated as bubble-type event listeners Next, two objects — the documentand a form — are given capture-type event listen-ers for the clickevent The documentobject event listener invokes the same function as the bubble-type event handler (the alert text includes some asterisks to remind you that it is the same alert being displayed in both the capture and bubble phases of the event) For the form object, however, the capture-type event listener
is directed to one function, while a bubble-type listener for the same object is directed at a separate function In other words, the form object invokes one func-tion as the event trickles down to the target and another funcfunc-tion when the event starts bubbling back up Many of the event handler functions dynamically read the
eventPhaseproperty of the event object to reveal which phase of event propaga-tion is in force at the instance the event handler is invoked
Listing 29-7: NN6 Event Capture and Bubble
<HTML>
<HEAD>
<TITLE>W3C DOM Event Propagation</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function init() { // using old syntax to assign bubble-type event handlers window.onclick = winEvent
document.onclick = docEvent document.body.onclick = docBodEvent // turn on click event capture for two objects document.addEventListener(“click”, docEvent, true) document.forms[0].addEventListener(“click”, formCaptureEvent, true) // set event listener for bubble
document.forms[0].addEventListener(“click”, formBubbleEvent, false) }
function winEvent(evt) { alert(“Event is now at the window object level (“ + getPhase(evt) + “).”) }
function docEvent(evt) { alert(“Event is now at the **document** object level (“ + getPhase(evt) +
“).”) } function docBodEvent(evt) { alert(“Event is now at the BODY level (“ + getPhase(evt) + “).”) }
function formCaptureEvent(evt) { alert(“This alert triggered by FORM only on CAPTURE.”) }
function formBubbleEvent(evt) { alert(“This alert triggered by FORM only on BUBBLE.”) }
// reveal event phase of current event object function getPhase(evt) {
switch (evt.eventPhase) {
Trang 2case 2:
return “AT TARGET”
break
case 3:
return “BUBBLING”
break
default:
return “”
}
}
</SCRIPT>
</HEAD>
<BODY onLoad=”init()”>
<H1>W3C DOM Event Propagation</H1>
<HR>
<FORM>
<INPUT TYPE=”button” VALUE=”Button ‘main1’” NAME=”main1” onClick=
“alert(‘Event is now at the button object level (‘ + getPhase(event) +
‘).’)”>
</FORM>
</BODY>
</HTML>
If you want to remove event capture after it has been enabled, use the
removeEventListener()method on the same object as the event listener that
was originally added (see Chapter 15) And, because multiple event listeners can be
attached to the same object, specify the exact same three parameters to the
removeEventListener()method as applied to the addEventListener()method
Preventing NN6 event bubbling or capture
Corresponding to the cancelBubbleproperty of the IE4+ eventobject is an
event object method in the W3C DOM The method that prevents propagation in
any event phase is the stopPropagation()method Invoke this method anywhere
within an event listener handler function The current function executes to
comple-tion, but the event propagates no further
Listing 29-8 extends the example of Listing 29-7 to include two checkboxes that
let you stop propagation type at the FORM element in your choice of the capture or
bubble phase
Listing 29-8: Preventing Bubble and Capture
<HTML>
<HEAD>
<TITLE>W3C DOM Event Propagation</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function init() {
// using old syntax to assign bubble-type event handlers
window.onclick = winEvent
Continued
Trang 3Listing 29-8 (continued)
document.onclick = docEvent document.body.onclick = docBodEvent // turn on click event capture for two objects document.addEventListener(“click”, docEvent, true) document.forms[0].addEventListener(“click”, formCaptureEvent, true) // set event listener for bubble
document.forms[0].addEventListener(“click”, formBubbleEvent, false) }
function winEvent(evt) {
if (evt.target.type == “button”) { alert(“Event is now at the window object level (“ + getPhase(evt) + “).”)
} } function docEvent(evt) {
if (evt.target.type == “button”) { alert(“Event is now at the **document** object level (“ + getPhase(evt) + “).”)
} } function docBodEvent(evt) {
if (evt.target.type == “button”) { alert(“Event is now at the BODY level (“ + getPhase(evt) + “).”) }
} function formCaptureEvent(evt) {
if (evt.target.type == “button”) { alert(“This alert triggered by FORM only on CAPTURE.”)
if (document.forms[0].stopAllProp.checked) { evt.stopPropagation()
} } } function formBubbleEvent(evt) {
if (evt.target.type == “button”) { alert(“This alert triggered by FORM only on BUBBLE.”)
if (document.forms[0].stopDuringBubble.checked) { evt.preventBubble()
} }
} // reveal event phase of current event object function getPhase(evt) {
switch (evt.eventPhase) { case 1:
return “CAPTURING”
break case 2:
return “AT TARGET”
break case 3:
return “BUBBLING”
Trang 4return “”
}
}
</SCRIPT>
</HEAD>
<BODY onLoad=”init()”>
<H1>W3C DOM Event Propagation</H1>
<HR>
<FORM>
<INPUT TYPE=”checkbox” NAME=”stopAllProp”>Stop all propagation at FORM<BR>
<INPUT TYPE=”checkbox” NAME=”stopDuringBubble”>Prevent bubbling past FORM
<HR>
<INPUT TYPE=”button” VALUE=”Button ‘main1’” NAME=”main1” onClick=
“alert(‘Event is now at the button object level (‘ + getPhase(event) +
‘).’)”>
</FORM>
</BODY>
</HTML>
Redirecting NN6 events
The mechanism for sending an event to an object outside the normal
propaga-tion pattern in NN6 is similar to that of IE4+, although with different syntax In place
of the IE4+ fireEvent()method, NN6 uses the W3C DOM dispatchEvent()
method The sole parameter of the method is an event object, such as the current
event object Listing 29-9 is the same as the IE4+ Listing 29-6, but with just a few
modifications to run in the NN6 event model Notice that the dispatchEvent()
method passes the current event object as its sole parameter
Listing 29-9: Cancelling and Redirecting Events in NN6+
<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.target.tagName + “ at “
msg += evt.clientX + “,” + evt.clientY + “) is now at the “
msg += elem + “ element.”
alert(msg)
}
function init() {
document.onclick = docEvent
document.body.onclick = docBodEvent
}
function docEvent(evt) {
revealEvent(“document”, evt)
}
Continued
Trang 5Listing 29-9 (continued)
function docBodEvent(evt) { revealEvent(“BODY”, evt) }
function buttonEvent(form, evt) { revealEvent(“BUTTON”, evt) // redirect if checked
if (form.redirect.checked) { document.body.dispatchEvent(evt) }
// cancel if checked
if (form.bubbleCancelState.checked) { evt.stopPropagation()
} }
</SCRIPT>
</HEAD>
<BODY onLoad=”init()”>
<H1>Event Cancelling & Redirecting</H1>
<HR>
<FORM onClick=”revealEvent(‘FORM’, event)”>
<P><BUTTON NAME=”main1” onClick=”buttonEvent(this.form, event)”>
Button ‘main1’
</BUTTON></P>
<P><INPUT TYPE=”checkbox” NAME=”bubbleCancelState”
onClick=”event.stopPropagation()”>Cancel Bubbling at BUTTON<BR>
<INPUT TYPE=”checkbox” NAME=”redirect” onClick=”event.stopPropagation()”> Redirect Event to BODY</P>
</FORM>
</BODY>
</HTML>
Referencing the event object
While there may be essentially three different event object models in today’s browsers, the way your scripts access those objects is divided into two camps: the
IE way; and the NN (and W3C) way I start with the simpler, IE way
IE4+ event object references
In IE4+, the eventobject is accessible as a property of the windowobject: window.event
But, as you are well aware, the windowpart of references is optional, so your scripts can treat the eventobject as if it were a global reference:
event.propertyName
Thus, any statement in an event handler function can access the eventobject without any special preparation or initializations
Trang 6The situation is a bit more complicated in the NN4+ event model In some cases
you must explicitly pass the event object as a parameter to an event handler
func-tion, while in other cases, the event object is delivered as a parameter
automati-cally The difference depends on how the event handler function is bound to the
object
Using the original way of binding event handlers to objects — via an attribute in
the element’s tag — you must specify the event object as a parameter by passing
eventas a parameter, as in
onClick=”doSomething(event)”
This is the only time in the NN4+ model that you see an explicit reference to the
event(lowercase “e”) object as if it were a global reference This reference does
not work in any other context — only as a parameter to an event handler function If
you have multiple parameters, the eventreference can go in any order, but I tend
to put it last:
onClick=”doSomething(this, event)”
The function definition that is bound to the element should therefore have a
parameter variable in place to “catch” the event object parameter:
function doSomething(widget, evt) { }
You have no restrictions on how you name this parameter variable In some
examples of this book, you may see the variable assigned as eventor, more
com-monly, evt When working with cross-browser scripts, avoid using eventas a
parameter variable name so as not to interfere with IE’s eventproperty
Other ways of binding event handler functions to objects — via property
assign-ments and the addEventListener()method in NN6+ — assign references of those
handlers to the desired objects in the document, as in either of the following:
document.forms[0].someButton.onclick = doSomething
document.getElementById(“myButton”).addEventListener(“click”, doSomething, false)
Event binding through these approaches prevents explicit passage of your own
parameters to the invoked functions But the NN4+ browsers automatically pass as
the sole parameter a reference to the event object created in response to the user
or system action that triggered the event This means that your functions should
“receive” the passed event object in a parameter variable:
function doSomething(evt) { }
Recall that the event object contains a reference to the object that was the target
of the event From that, you can access any properties of that object, such as the
form object that contains a form control object
You can see the way the event object is passed as a parameter in Listing 29-9 For
all event handlers that are assigned by reference (both to an event handler property
of an object and to an addEventListener()method call), the functions have a
parameter variable in place to act as a reference to the event object for statements
within the function If you need to invoke other functions from there, you can pass
the event object reference further along as needed The event object retains its
prop-erties as long as the chain of execution triggered by the event action continues
Trang 7event Object Compatibility
Despite the incompatible ways that NN and IE event objects arrive at an event handler function, you can easily stuff the object into one variable that both browser types can use For example, the following function fragment receives an event object from NN but also accommodates the IE eventobject:
function doSomething(evt) { evt = (evt) ? evt : (window.event) ? window.event : “”
if (evt) { // browser has an event to process
} }
If an event object arrives as a parameter, it continues to be available as evt; but
if not, the function makes sure that a window.eventobject is available and assigns
it to the evtvariable; finally, if the browser doesn’t know about an eventobject, the evtvariable is made an empty string Processing continues only if evtcontains
an event object
That’s the easy part The madness comes in the details: reading properties of the event object when the property names can vary widely across the three event object models Sections later in this chapter provide details of each property and method of all three event object models, but seeing an overview of the property ter-minology on a comparative basis is helpful Table 29-2 lists the common informa-tion bits and acinforma-tions you are likely to want from an event object and the property
or method names used in the three event object models
Table 29-2 Common event Object Properties and Methods
Target element target srcElement target
X coordinate in element n/a † offsetX n/a †
Y coordinate in element n/a † offsetY n/a †
positioned element
positioned element
X coordinate on page pageX n/a † pageX
Y coordinate on page pageY n/a † pageY
X coordinate in window n/a clientX clientX
Y coordinate in window n/a clientY clientY
X coordinate on screen screenX screenX screenX
Trang 8Y coordinate on screen screenY screenY screenY
Shift key pressed modifiers shiftKey shiftKey
Alt key pressed modifiers altKey altKey
Ctrl key pressed modifiers ctrlKey ctrlKey
Previous Element n/a fromElement relatedTarget
Next Element n/a toElement relatedTarget
Cancel bubbling n/a cancelBubble preventBubble()
Prevent default action return false returnValue preventDefault()
†Value can be derived through calculations with other properties.
As you can see in Table 29-2, properties for the IE4+ and NN6 event objects have
a lot in common This is good news, especially as the installed base of NN4 users
diminishes over time The primary incompatibility is how to reference the element
that is the intended target of the event This, too, can be branched in your code to
achieve a common variable that references the element For example, embedded
within the previous function fragment can be a statement, such as the following:
var elem = (evt.target) ? evt.target : evt.srcElement
Each event model has additional properties that are not shared by the other
Details about these are covered in the rest of this chapter
Dueling Event Models
Despite the sometimes widely divergent ways event object models treat their
properties, accommodating a wide range of browsers for event manipulation is not
difficult In this section, you see two scripts that examine important event
proper-ties The first script reveals which, if any, modifier keys are held down during an
event; the second script extracts the codes for both mouse buttons and keyboard
keys Both scripts work with all browsers that have event objects, including NN4 If
your audience no longer uses NN4, you can eliminate the code branches that
sup-port it
Cross-platform modifier key check
Listing 29-10 demonstrates branching techniques for examining the modifier
key(s) being held down while an event fires Details of the event object properties,
such as modifiersand altKey, can be found later in this chapter To see the page
in action, click a link, type into a text box, and click a button while holding down
any combination of modifier keys A series of four checkboxes representing the four
modifier keys is at the bottom As you click or type, the checkbox(es) of the
pressed modifier key(s) become checked
Trang 9Listing 29-10: Checking Events for Modifier Keys
<HTML>
<HEAD>
<TITLE>Event Modifiers</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function checkMods(evt) { evt = (evt) ? evt : (window.event) ? window.event : “”
if (evt) { var elem = (evt.target) ? evt.target : evt.srcElement var form = document.output
if (evt.modifiers) { form.modifier[0].checked = evt.modifiers & Event.ALT_MASK form.modifier[1].checked = evt.modifiers & Event.CONTROL_MASK form.modifier[2].checked = evt.modifiers & Event.SHIFT_MASK form.modifier[3].checked = evt.modifiers & Event.META_MASK } else {
form.modifier[0].checked = evt.altKey form.modifier[1].checked = evt.ctrlKey form.modifier[2].checked = evt.shiftKey form.modifier[3].checked = false }
} return false }
</SCRIPT>
</HEAD>
<BODY>
<H1>Event Modifiers</H1>
<HR>
<P>Hold one or more modifier keys and click on
<A HREF=”javascript:void(0)” onMouseDown=”return checkMods(event)”>
this link</A> to see which keys you are holding.</P>
<FORM NAME=”output”>
<P>Enter some text with uppercase and lowercase letters:
<INPUT TYPE=”text” SIZE=40 onKeyUp=”checkMods(event)”></P>
<P><INPUT TYPE=”button” VALUE=”Click Here With Modifier Keys”
onClick=”checkMods(event)”></P>
<P>
<INPUT TYPE=”checkbox” NAME=”modifier”>Alt
<INPUT TYPE=”checkbox” NAME=”modifier”>Control
<INPUT TYPE=”checkbox” NAME=”modifier”>Shift
<INPUT TYPE=”checkbox” NAME=”modifier”>Meta
</P>
</FORM>
</BODY>
</HTML>
Because all three event handlers call the same checkMods()function, branching
is needed only in this function Notice, though, that branching is done by object detection, rather than navigator.userAgentdetection This method makes the most sense for this example, because the scripts rely on the existence of particular objects and properties for their proper execution For NN4, the event object is
Trang 10the event object property for each of three modifiers.
Cross-platform key capture
To demonstrate keyboard events in both browsers, Listing 29-11 captures the
key character being typed into a text box, as well as the mouse button used to click
a button As with Listing 29-10, NN4 has a very different way of getting this
informa-tion compared to IE4+ and NN6 In this arena, however, NN6 continues to support
the NN4 syntax as well, so you can use the old or new syntax as you like Whereas
NN4 combines the features of key character code and mouse button into one event
object property (depending upon the event type), newer browsers have entirely
separate properties for these values Listing 29-11 is written such that NN6 follows
the NN4 syntax path, but even if the NN4 syntax should disappear in a future NN
version, the browser would follow the new syntax path without blinking an eye
Listing 29-11: Checking Events for Key and Mouse Button
Pressed
<HTML>
<HEAD>
<TITLE>Button and Key Properties</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function checkWhich(evt) {
evt = (evt) ? evt : (window.event) ? window.event : “”
if (evt) {
var thingPressed = “”
var elem = (evt.target) ? evt.target : evt.srcElement
if (evt.which) {
thingPressed = evt.which
} else {
if (elem.type == “textarea”) {
thingPressed = evt.keyCode } else if (elem.type == “button”) {
thingPressed = evt.button }
}
status = thingPressed
}
return false
}
</SCRIPT>
</HEAD>
<BODY>
<H1>Button and Key Properties</H1> (results in the status bar)
<HR>
<FORM>
<P>Mouse down atop this
<INPUT TYPE=”button” VALUE=”Button” onMouseDown=”checkWhich(event)”>
this link</A> or this
<INPUT TYPE=”button” VALUE=”Button” onMouseDown=”checkWhich(event)”>
Continued