Web DevCenter
oreilly.comSafari Books Online.Conferences.
MySQL Conference and Expo April 14-17, 2008, Santa Clara, CA

Sponsored Developer Resources

Web Columns
Adobe GoLive
Essential JavaScript

Web Topics
All Articles
Scripting Languages

Atom 1.0 Feed RSS 1.0 Feed RSS 2.0 Feed

Learning Lab

Supporting Three Event Models at Once
Pages: 1, 2, 3

Event Binding III: IE4+ <SCRIPT FOR> Tags

In IE4+, Microsoft implements its own extensions to the <SCRIPT> tag that bind the enclosed script statements to one event type for one element. The tag's attributes that make this binding possible (not sanctioned by the W3C for HTML) are FOR and EVENT.

The value of the FOR attribute must be the unique identifier you assign to an element's ID attribute. You must then assign the name of the event (onmouseover, onclick, etc) to the EVENT attribute. Using the button example above, the button's tag must be modified to include an ID attribute:

<INPUT TYPE="button" NAME="myButton" ID="button1" VALUE="Click Here">

Script statements are not in a function, but within the <SCRIPT> tag, as follows:

<SCRIPT FOR="button1" EVENT="onclick">
// script statements here

Naturally, statements inside the tag can call any other functions defined elsewhere on the page (or imported from a .js file). But this binding style means that you must create a <SCRIPT FOR> tag for each element and each event.

You must also be careful to deploy this binding approach only on pages that will be viewed by IE4+ browsers. Any other scriptable browser (including IE3) that doesn't implement this special kind of <SCRIPT> tag treats the tag as a regular <SCRIPT> tag, and attempts to execute the statements inside while the page loads -- inevitably leading to script errors.

Event Binding IV: The IE5/Windows attachEvent() Method

Implemented before the W3C DOM working group honed the standard event model, the attachEvent() method is available for every HTML element object in the Windows version of IE5 and later. In other words, to bind an event to an element object, invoke the attachEvent() method on that object.

The syntax for the attachEvent() method is as follows:

elemObject.attachEvent("eventName", functionReference);

Values for the eventName parameter are strings representing the event name, such as onmousedown. The functionReference parameter is the same kind of parenthesis-free reference to a function described earlier in the event property approach. Thus, for the button object I've been using as an example, the statement that binds the function to the button's click event is as follows:

document.getElementById("button1").attachEvent("onclick", myFunc);

Since the attachEvent() method works strictly in the IE5+/Windows environment, you can use either the W3C DOM element reference (above) or the IE4+ reference:

document.all.button1.attachEvent("onclick", myFunc);

One caveat about this approach: You cannot permit the statement to execute before the element's tag loads in the browser. The reference to the button object is not valid until the HTML creates the element in the browser. Therefore, such binding statements need to run either at the bottom of the page or in a function invoked by the onLoad event handler of the BODY element.

Event Binding V: The W3C DOM addEventListener() Method

Netscape 6 adopts the W3C DOM Level 2 event binding mechanism, which, while similar to the IE5/Windows attachEvent() approach, has its own syntax. The W3C DOM specification assigns the addEventListener() method to every node in the DOM hierarchy. While an HTML element is a type of DOM node, a text node inside an element's tag set is also a node capable of receiving events. This is an important point that haunts NN6 event processing, as you will see later in this article.

The syntax for the addEventListener() method is as follows:

nodeReference.addEventListener("eventType", listenerReference, captureFlag);

In the jargon of the W3C DOM specification, the addEventListener() method registers an event with a node, instructing the node what to do with the event. The first parameter of the method is a string declaring the type of event (without the "on"), such as click, mousedown, and keypress. Treat the second parameter of addEventListener() in the same manner as the function reference described earlier. The third parameter is a Boolean value that signifies whether the node should listen for the event in what the DOM calls capture mode. The subject of event capture and bubbling -- collectively called event propagation -- is subject best left for another article. For a typical event listener, the third parameter should be false.

Which Binding is Best?

If you're lucky enough to be creating applications for a single browser version and operating system, you can opt for the most modern binding for the chosen browser. Cross-browser authors, however, have a substantial challenge.

If you plan to support IE5/Mac, you can dismiss the attachEvent() and addEventListener() methods because IE5/Mac supports neither of these. Your practical choice, then, is between the tag attribute and object property approaches. This is where psychological conflicts come into play.

On the one hand, the tag attribute approach is acknowledged by the W3C DOM Level 2 recommendation as an acceptable substitute for the addEventListener() method. To be compatible with millions of existing scripts, all scriptable browsers support tag attribute binding. Automated authoring tools, such as DreamWeaver, also embed event handler attributes in HTML tags.

On the other hand, embedding script-oriented information within an element's tag flies in the face of the compelling trend toward separating content from style and behavior. Binding events as object properties heads in the right direction, but there is no "official" support for event properties in W3C standards related to HTML, XHTML, or DOM. The approach is, however, supported in real life by all but the first-generation scriptable browsers.

A standards purist will find fault with either approach; but a practical developer should feel "safe" with either approach, even when considering compatibility with future mainstream browsers.

The Event Mother Lode: The Event Object

At the core of all three event models is an event object -- an abstract entity whose properties contain a ton of information that is potentially valuable to functions that process events. From the discussion of event binding techniques earlier in this article, you might deduce one reason why the event object is so vital to scripts: with the exception of tag attribute binding, no binding approach permits parameter passing to the event handler function.

The event object fills the gap by providing enough "hooks" to let a function read event characteristics. Thus, a function can retrieve a reference to the element that received the event and other tidbits, such as coordinates of mouse actions, mouse button(s) used, keyboard keys pressed, and whether any modifier keys were held down during the event (for example, to detect a Shift-click).

Accessing the Event Object

Although the precise composition of the event object varies according to the three DOMs discussed throughout this article (NN4, IE4+, and W3C/NN6), an event handler function can access an event object in only one of two ways: the NN way and the IE way. The W3C/NN6 DOM event object exposes itself to scripts the same way the NN4 event object does; IE4+, on the other hand, has a different approach.

The IE4+ event object is easier to describe, so we'll cover that first. Simply said, the event object is a property of the window object. By implication, this means that only one event object exists at any instant. For example, the simple act of pressing and releasing a keyboard key generates three events: onKeyDown, onKeyPress, and onKeyUp (in that order). If a function invoked by the onKeyDown event takes a long time to process, the browser holds the other two events in a queue until all of the onMouseDown event's function processing completes.

On the NN4 and W3C DOM sides, the event object seems even more abstract. For all event binding techniques except the tag attribute style, the event object gets passed automatically to the function bound to the event. A single parameter is sent to the function. It is your job to provide a parameter variable in the function definition to "receive" that parameter value. To avoid conflict with the IE window.event object, do not use event as the parameter name. The variable name evt is as good as any. Such a function definition looks like the following:

function myFunc(evt) {
    // script statements here

If you use the tag attribute event binding technique, however, you must explicitly pass the event as one of the parameters of your function call. To do this, use the event keyword as the parameter:

onClick = "myFunc(event)"

The incoming parameter variable is your function's only connection to the NN event object. If the object or its values are needed in another function that is invoked from within the main event handler function, you must relay the object or its property value(s) as a parameter(s) to the other functions.

If you're wondering whether IE would plug the window.event property into this event reference, the answer is, "Yes." This syntax overlap is a safe because both the NN and IE event objects that get passed to the function have the desired property values of the current event.

Accommodating Both Event Object References

Assuming that a function needs to inspect one or more properties of an event object to process the event, a simple technique lets your function work with the event object passed as a parameter or read from the window.event property. Moreover, the technique doesn't require an ounce of browser version sniffing.

To begin, always define your event handler functions with a parameter variable to prepare for the possibility of an incoming event object. Then use a simple conditional expression to assign the event object of the browser to that parameter variable:

function myFunc(evt) {
  evt = (evt) ? evt : ((window.event) ? window.event : "")
  // process event here

If the event object arrives as a parameter, it remains assigned to the evt local variable inside the function. But if the parameter is null and the browser's window object includes an event property, then the window.event object assigns itself to the evt variable.

To complete the job, however, you should also include one more conditional layer that gracefully handles earlier browsers lacking an event object in their object models:

function myFunc(evt) {
  evt = (evt) ? evt : ((window.event) ? window.event : "")
  if (evt) {
      // process event here

By building all event handler functions this way, you define a function that accommodates event objects passed explicitly from tag attribute binding or implicitly from event property binding. Even if you change the binding style during development, the function doesn't have to change.

Event Object Smorgasbord

Establishing a reference to the event object is only part of the battle, however. Each event object from each event model has its own set of properties containing event details. The table below lists the most commonly accessed properties and the names of those properties in the three primary event object types.

Table 1. Popular Event Object Properties
Event targettargetsrcElementtarget
Event typetypetypetype
X coordinate on pagepageX*pageX
Y coordinate on pagepageY*pageY
Mouse buttonwhichbuttonbutton
Keyboard keywhichkeyCodekeyCode

*Values can be calculated by evaluating event.clientX + document.body.scrollTop or event.clientY + document.body.scrollTop.

IE5 for the Macintosh generally follows the IE4+ event object. The one exception is that the IE5/Mac event object features both a srcElement and target property to refer to the element that receives the event.

Perhaps the most important event object property to extract is a reference to the HTML element that receives the event. The NN4 and W3C event objects share the same property name (target), while the IE4+ event object uses srcElement. Object detection (instead of laborious and hazard-prone browser version sniffing) comes to the rescue again. For elements that are not text containers, a simple conditional expression handles the syntactic differences with ease:

var elem = (evt.target) ? evt.target : evt.srcElement

From here, your script can read and/or write whatever element object properties that the browsers object model exposes.

Pages: 1, 2, 3

Next Pagearrow