The event model of Internet Explorer 4 and later is more advanced than that of Netscape 4. Because every element in the page is represented as an object under IE4+, a richer, more robust set of elements are capable of generating events. In addition, Microsoft has implemented a wider variety of events that apply to each object. One major downside is that event propagation occurs in the opposite manner as with Netscape 4, complicating cross-browser programming in environments where backwards compatibility is important.
No matter what browser the user has, you can always attach event handlers to objects using (X)HTML attributes specified directly in the element or with JavaScript. But Internet Explorer provides an additional mechanism for doing so: the attachEvent() method. This method was added to all document objects in Internet Explorer 5 to support DHTML Behaviors (Chapter 21), and probably in anticipation of the DOM2 standard as well (though the semantics of its DOM2 cousin are much different).
The attachEvent() method has the following syntax,
object.attachEvent(“event to handle“, eventHandler);
where the first parameter is a string like “onclick” and eventHandler is the function that should be invoked when the event occurs. The return value is a Boolean indicating whether attachment was successful.
To remove a handler bound this way, use detachEvent() with the exact same arguments. The following simple example illustrates the syntax:
<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">> <<html xmlns="http://www.w3.org/1999/xhtml">> <<head>> <<title>>IE Attach/Detach Event Test<</title>> <<meta http-equiv="content-type" content="text/html; charset=utf-8" />> <<script type="text/javascript">> <<!-- function showAuthor() { alert("Oscar Wilde"); } function enableEvent() { someText.attachEvent("onmouseover", showAuthor); } function disableEvent() { someText.detachEvent("onmouseover", showAuthor); } //-->> <</script>> <</head>> <<body onload="enableEvent();">> <<em id="someText">>We may be in the gutter, but some of us are looking at the stars<</em>> <<form action="#" method="get">> <<input type="button" value="Attach Event" onclick="enableEvent();" />> <<input type="button" value="Detach Event" onclick="disableEvent();" />> <</form>> <</body>> <</html>>
Note |
You can bind multiple handlers for the same event to a single object using attachEvent(). However, there is no guarantee on the order in which the handlers will be called. |
Similar to Netscape 4, when an event occurs in Internet Explorer, the browser creates a transient Event object and makes it available to the appropriate handler. Unlike Netscape 4, it is implicitly made available as the global variable event. Some properties of the object are listed in Table 11-8.
Property |
Description |
---|---|
>srcElement |
Reference to the object for which the event is intended (i.e., the event's target). |
>Type |
String containing the type of event, e.g., "click". |
>clientX |
Numeric value indicating the horizontal coordinate of the event. |
>clientY |
Numeric value indicating the vertical coordinate of the event. |
>screenX |
Numeric value indicating the horizontal coordinate of the event relative to the whole screen. |
>screenY |
Numeric value indicating the vertical coordinate of the event relative to the whole screen. |
>button |
Numeric value indicating the mouse button pressed (primary is 0, but varies from system to system). |
>keyCode |
Numeric value indicating the Unicode value of the key depressed. |
>altKey |
Boolean indicating if the Alt key was depressed. |
>ctrlKey |
Boolean indicating if the Ctrl key was depressed. |
>shiftKey |
Boolean indicating if the Shift key was depressed. |
>cancelBubble |
Boolean indicating whether the event should not bubble up the hierarchy. |
>returnValue |
Boolean indicating the return value from the event handler. Other handlers |
>fromElement |
Reference to the element the mouse is moving away from in a mouseover |
>toElement |
Reference to the element the mouse is moving to during mouseover or mouseout. |
Since the Event object is implicitly available everywhere, there’s no need to pass it to a handler bound with JavaScript. However, there’s no harm in doing so, and the practice means that your scripts will work with both Netscape 4 and IE4+.
The flow of events in Internet Explorer is the opposite of Netscape 4. Most events begin at the object at which they occur and bubble up the hierarchy. Bubbling events give the appropriate handler at each level in the hierarchy the opportunity to handle, redirect, or pass the event along up the tree. Bubbling events proceed up to the Document, but there they stop (i.e., they don’t propagate up to the Window).
Some events that have specific, well-defined meanings, such as form submission and receiving focus, do not bubble. Whereas bubbling events work their way up the tree, causing the appropriate handler to be invoked at each level in the hierarchy until they reach the top or are canceled, non-bubbling events invoke the handler only of the object at which they occur. The rationale is that such events do not have well-defined semantics at a higher level in the hierarchy, so they should not be propagated up the tree. The list of Internet Explorer events and their bubbling behavior is given in Table 11-9.
Event Handler |
Bubbles? |
Cancelable? |
---|---|---|
onabort |
No |
Yes |
onactivate |
Yes |
No |
onafterprint |
No |
No |
onafterupdate |
Yes |
No |
onbeforeactivate |
Yes |
Yes |
onbeforecopy |
Yes |
Yes |
onbeforecut |
Yes |
Yes |
onbeforedeactivate |
Yes |
Yes |
onbeforeeditfocus |
Yes |
Yes |
onbeforepaste |
Yes |
Yes |
onbeforeprint |
No |
No |
onbeforeunload |
No |
Yes |
onbeforeupdate |
Yes |
Yes |
onblur |
No |
No |
onbounce |
No |
Yes |
oncellchange |
Yes |
No |
onchange |
No |
Yes |
onclick |
Yes |
Yes |
oncontextmenu |
Yes |
Yes |
oncontrolselect |
Yes |
Yes |
oncopy |
Yes |
Yes |
oncut |
Yes |
Yes |
ondataavailable |
Yes |
No |
ondatasetchanged |
Yes |
No |
ondatasetcomplete |
Yes |
No |
ondblclick |
Yes |
Yes |
ondeactivate |
Yes |
No |
ondrag |
Yes |
Yes |
ondragend |
Yes |
Yes |
ondragenter |
Yes |
Yes |
ondragleave |
Yes |
Yes |
ondragover |
Yes |
Yes |
ondragstart |
Yes |
Yes |
ondrop |
Yes |
Yes |
onerror |
No |
Yes |
onerrorupdate |
Yes |
No |
onfilterchange |
No |
No |
onfinish |
No |
Yes |
onfocus |
No |
No |
onfocusin |
Yes |
No |
onfocusout |
Yes |
No |
onhelp |
Yes |
Yes |
onkeydown |
Yes |
Yes |
onkeypress |
Yes |
Yes |
onkeyup |
Yes |
No |
onlayoutcomplete |
Yes |
Yes |
onload |
No |
No |
onlosecapture |
No |
No |
onmousedown |
Yes |
Yes |
onmouseenter |
No |
No |
onmouseleave |
No |
No |
onmouesmove |
Yes |
No |
onmouseout |
Yes |
No |
onmouseover |
Yes |
Yes |
onmouseup |
Yes |
Yes |
onmousewheel |
Yes |
Yes |
onmove |
Yes |
No |
onmoveend |
Yes |
No |
onmovestart |
Yes |
Yes |
onpaste |
Yes |
Yes |
onpropertychange |
No |
No |
onreadystatechange |
No |
No |
onreset |
No |
Yes |
onresize |
No |
No |
onresizeend |
Yes |
No |
onresizestart |
Yes |
Yes |
onrowenter |
Yes |
No |
onrowexit |
No |
Yes |
onrowsdelete |
Yes |
No |
onrowsinserted |
Yes |
No |
onscroll |
No |
No |
onselect |
No |
Yes |
onselectionchange |
No |
No |
onselectstart |
Yes |
Yes |
onstart |
No |
No |
onstop |
No |
No |
onsubmit |
No |
Yes |
onunload |
No |
No |
You might wonder about the cancelable column in Table 11-9. The idea here is that an event that is cancelable can have its upward progress halted in script. We’ll see how to do this in a moment, but for now to illustrate event bubbling in action, consider the following example. Handlers for clicks are defined for many objects in the hierarchy, and each writes the name of the element to which it is attached into the paragraph with id of “results”:
<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">> <<html xmlns="http://www.w3.org/1999/xhtml">> <<head>> <<title>>Event Bubbling Example<</title>> <<meta http-equiv="content-type" content="text/html; charset=utf-8" />> <<script type="text/javascript">> <<!-- function gotClick(who) { document.all.results.innerHTML += who + " got the click <<br />>"; } //-->> <</script>> <</head>> <<body onclick="gotClick('body');">> <<table onclick="gotClick('table');">> <<tr onclick="gotClick('tr');">> <<td onclick="gotClick('td');">> <<p onclick="gotClick('p');">> Click on the <<b onclick="gotClick('b');">>BOLD TEXT<</b>> to watch bubbling in action! <</p>> <</td>> <</tr>> <</table>> <<hr />> <<br />> <<p id="results">> <</p>> <</body>> <</html>>
Clicking the bold text causes a click event to occur at the <<b>> tag. The event then bubbles up, invoking the onclick handlers off objects above it in the containment hierarchy. The result is shown in Figure 11-1.
You can stop events from propagating up the hierarchy by setting the cancelBubble property of the Event object. This property is false by default, meaning that after a handler is finished with the event, it will continue on its way up to the next enclosing object in the hierarchy. Setting the cancelBubble property to true prevents further bubbling after the current handler has finished. For example, you could prevent the event from getting beyond the <<b>> tag in the last example by making this small modification:
...<<b onclick="gotClick('b');event.cancelBubble=true;">>BOLD TEXT<</b>>...
The result of clicking on the bold text after this change is made is shown in Figure 11-2.
Not all events are cancelable; Table 11-9 indicates whether each event can be canceled in this way.
It is important to keep in mind that returning false from a handler (or setting event.returnValue to false) prevents the default action for the event, but does not cancel bubbling. Later handlers invoked by the bubbling behavior will still have a chance to handle the event, and any value they return (or set event.returnValue to) will “overwrite” the value set or returned by a previous handler.
Conversely, canceling bubbling does not affect the event’s return value. Because the default returnValue of an event is true, you need to be sure to return false or set returnValue to false if you wish to prevent the event’s default action.
Because all parts of the page are scriptable in IE4+, performing event captures as in Netscape 4 is very easy. Simply set the handler at the appropriate level in the hierarchy, for example, to capture clicks at the Document level with the function myHandler:
document.onclick = myHandler;
and omit Click handlers from lower objects. To unset event capture, simply set the appropriate handler to null, for example, to turn off click capturing at the Document level:
document.onclick = null;
Events bubble up strictly through objects in the hierarchy that contain them. There is, however, a primitive way to redirect to another object in Internet Explorer 5.5+. Each object has a fireEvent() method that transfers the event to the object on which it is invoked:
object.fireEvent(“event to fire“ [, eventObject])
The first argument is a string denoting the handler to fire, for example, "onclick". The optional eventObject parameter is the Event object from which the new Event object will be created. If eventObject is not given, a brand new Event is created and initialized as if the event had really occurred on the target object. If eventObject is specified, its properties are copied into the new Event object, except for cancelBubble, returnValue, srcElement, and type. These values are always initialized (respectively) to false, true, the element on which the event is firing, and the type of event given by the first argument to fireEvent().
One major downside of this method is that its invocation causes a new Event to be created, so the reference to the original target (event.srcElement) is lost during the handoff.
The following example illustrates the method:
function handleClick() { event.cancelBubble = true; // Redirect event to the first image on the page document.images[0].fireEvent("onclick", event); }
When set as a click handler, the prceding function redirects the event to the first image in the page.
Remember to cancel the original event before redirecting to another object; failing to do so “forks” the event by allowing it to continue on its way up the hierarchy while adding the new event created by fireEvent() to the event queue. The new event will be fired only after the original event has finished bubbling.
In the basic event model, you can simulate events by invoking event handlers directly as well as implicitly create a few “real” events by invoking methods like submit() and focus(). Netscape 4 provided more flexibility with its routeEvent() method. Internet Explorer 5.5+ goes well beyond these capabilities by providing a way to create actual Event objects. The syntax is
var myEvent = document.createEventObject([eventObjectToClone]);
This createEventObject() method of the Document object returns an Event object, cloning the eventObjectToClone argument if one exists. You can set the properties of the newly created Event and cause the event to occur on an object of your choice by passing it as an argument to fireEvent().
While most programmers won’t really have cause to use this feature, the ability to create arbitrary events and cause them to occur on any element in the document hierarchy can be quite handy if you’re writing JavaScript-based applications. For example, they’re a useful base on top of which to build a generic JavaScript message-passing system, and can also be used to create user interface tests of complex sequences of user actions that would be laborious to trigger by hand.
Internet Explorer—especially versions 5.5 and later—provides more event-related features than we’ve covered here. Most of these features involve the proprietary event handlers IE supports for special user actions like dragging and dropping, printing, and so forth. To learn more, visit http://msdn.micrsosoft.com.