★ wanayoo — archive 1999 http://developer.iplanet.com/viewsource/goodman_events2/goodman_events2.htmlNouvelle recherche | Portail wanayoo
iPlanet

You are here:  Home > Developers > View Source Articles > JavaScript View Source Article
JavaScript View Source Article
 iPlanet Developers


Developer Program
  Membership
  One-to-One Support
  Newsgroups
  Member Services

Developer Publications
  View Source
  Developer News

Documentation
  Technical Manuals
  White Papers
  TechNotes
  Sample Code
  FAQs
  Books

Technologies
  Application Server   CSS
  DOM
  CORBA
  Directory & LDAP
  Dynamic HTML
  Java
  JavaScript
  Linux
  RDF
  Security
  SSJS
  XML
  XUL

Developer Downloads
  Tools & SDKs
  Patches

iPlanet Products
  Technical Resources










spacer
The JavaScript Apostle
Dueling Event Models: A Cross-Platform Look

By Danny Goodman


Send comments and questions about this article to View Source.
Click here for printer-friendly version  

The last time I wrote about events here in View Source (Getting Ready for JavaScript 1.2 Events), it was in anticipation of the upgraded event model in Netscape Communicator. That model has been realized (with some new items that weren't in the prerelease version available at the time), and scripts do a nice job with events when the page is intended for a Communicator-only audience. But if visitors to your site include Internet Explorer 4 (IE4) users, you've discovered that some of the advanced features of Communicator's event model don't work the same way in IE4. As I'll show in this article, it is possible to take advantage of both browsers' event models in the same document. This will become more important to you over time as you add scripted Dynamic HTML (DHTML) designs to your pages.

BASIC EVENTS

Events are the lifeblood of scripting and interactivity in a document. They're the initiators, the links between the user and the document. The number and granularity of events available to JavaScript increased substantially in Communicator. With one exception (the DragDrop event, whose utility in Communicator requires signed scripts), all of the events in Communicator are supported in IE4. Table 1 shows the events supported in both browsers and the objects from both browsers that support them. Each browser has additional event or object support that is not cross-platform, but as the table proves, the basics are well covered where the two browsers meet.


Table 1. Events supported in Communicator and IE4
Event Objects
abort image
blur window, text, textarea, password, select
change text, textarea, select
click link, area, button, radio, checkbox, reset, submit
dblclick link
error window, image
focus window, text, textarea, password, select
keydown text, textarea, password
keypress text, textarea, password
keyup text, textarea, password
load window, image
mousedown link, button, radio, checkbox, reset, submit
mouseout link, area
mouseover link, area
mouseup link, button, radio, checkbox, reset, submit
move window
reset form
resize window
select text, textarea, password
submit form
unload window

One event not listed in Table 1 is the mousemove event. This important DHTML event is supported in both browsers but in different ways, as I discuss later in this article.

ASSIGNING EVENT HANDLERS TO OBJECTS

Probably the most common way of assigning a script action to an event is via the HTML attribute associated with that event. Event attributes combine the word "on" with the event name (for example, onClick). You can assign an in-line script inside the tag, as in

<INPUT TYPE="button" NAME="myButton" VALUE="Click Here" onClick="alert('Howdy')">

or invoke a separate script function defined elsewhere in the document:

<INPUT TYPE="button" NAME="myButton" VALUE="Click Here" onClick="handleClick()">

For all of the events and objects listed in Table 1, you can add the associated event attribute to the object's tags and expect identical behavior in both browsers.

A second way of assigning event handler functions to objects first became available in Navigator 3 and is available in both Communicator and IE4. The same event handlers you know from HTML tag assignment can also be set as properties of the object. To be compatible with both browsers, make sure the name of the event handler in these assignment statements is the all-lowercase version of the HTML attribute, as in

document.forms[0].myButton.onclick = handleClick

The right side of this kind of assignment statement contains a reference to the function to be invoked when the event fires. Such references omit the parentheses normally associated with functions. One other caveat about this type of event handler assignment: the assignment statement must appear in the document after the function and the object have been defined. Otherwise a script error pops up as the document loads.

If you've looked at DHTML code written exclusively for IE4, you may have seen one additional way that this browser links a script to an object and event. In IE4, a <SCRIPT> tag can be earmarked for a specific event of a specific object by way of extra tag attributes that Communicator ignores. The tag set contains only statements that are to run when the IE4 object's event fires (that is, there is no function definition inside that tag set). You cannot use this assignment technique in a cross-platform page, however, because Communicator treats the tag like any other <SCRIPT> tag, and runs the statements as the document loads. Destination: Script Error City.

EVENT PROPAGATION

If you've been comforted so far by how much the two browsers have in common, please take a seat and fasten your seatbelt. The ride gets a little bumpy from here on.

Not every page must concern itself with event propagation, but for interactive content that features a large number of objects sharing scripted behaviors, it can be much more convenient to dissect and operate on all related events at an object higher up the document object model hierarchy. Instead of assigning similar or identical event handlers to a dozen objects, one event handler in, say, the document object takes care of the whole thing.

For efficient handoff of an event to another object, the browser must have a mechanism that allows an event to traverse the hierarchy. Both Communicator and IE4 have such mechanisms. Unfortunately for cross-platform development, each mechanism is the antithesis of the other: Communicator events trickle down the hierarchy; IE4 events bubble up the hierarchy.

Trickle-Down Events

Communicator's event-trickling mechanism is described more fully in Getting Ready for JavaScript 1.2 Events, but I'll hit the highlights here to help you understand the differences between Communicator and IE4 event propagation.

When a user clicks a button viewed in Communicator, the browser initially sends that event to the window object. From there it goes to the document object. Lastly, it reaches the target of the click, where an onClick event handler can process the event. But if you want the event to be processed or preprocessed in the window or document level, you must turn on event capture for either object (or both). Window and document objects in Communicator have a captureEvents() method that lets you specify precisely which event types the objects should grab as they pass through. Event types are properties of the Event (capital "E") object. Therefore, for the document object to capture the click event intended for a document's button, you tell the document to capture all click events:

document.captureEvents(Event.CLICK)

You must also assign an onClick event handler to the document object:

document.onclick = handleClick

With these statements in place in the loaded document, when a user clicks on any object that is capable of reacting to a click event, the handleClick() function is invoked. It is up to the statements inside the handleClick() function to examine more details about the event (as described later in Event Objects). This function can do its work and simply gobble up the event, or it can route the event to its intended target (or another target, for that matter).

To summarize Communicator's propagation mechanism:

  1. Events start at the window level and automatically go to the intended target object unless the window or document object is explicitly instructed to capture that event type.
  2. A capturing window or document level must have an event handler assigned to that object for that object type.
  3. Whether the event ultimately reaches its intended target is up to the event handler assigned to the window or document object that captures that event.

Bubble-Up Events

Internet Explorer 4's propagation mechanism initially directs an object's event to the intended target. The event invokes an event handler if one is defined for that object. Under normal conditions, after that event handler function does its thing, the event then continues up the containment hierarchy.

I must point out that there is a distinction between the document object hierarchy you're accustomed to in Communicator and IE4's containment hierarchy. The latter is based on HTML elements that are containers (that is, that have start and end tags). For example, consider the following skeletal document:

<HTML>
<BODY>
<FORM>
<DIV>
<INPUT TYPE="button">
</DIV>
</FORM>
</BODY>
</HTML>

In IE4, virtually every container element can have an onClick event handler assigned to it. Therefore, if a user clicks the button, the click event traverses the container hierarchy, passing through <DIV>, <FORM>, and <BODY> in that order. If you want the document-level object to handle all click events from several related buttons, you can place a single onClick event handler in the <BODY> tag and omit onClick event handlers for the buttons and all intervening objects.

To prevent an event from bubbling up, you must cancel event bubbling for the current event. This gets into the event (lowercase "e") object discussion coming up, but here is one way you can keep a click event held within a button object:

<INPUT TYPE="button" ... onClick="handleClick(); window.event.cancelBubble=true">

You can also include the cancelBubble statement inside the function invoked by the event handler. It affects only the current event being handled.

To summarize IE4's propagation mechanism:

  1. An event starts at its intended target and invokes an event handler if one is defined.
  2. Unless explicitly instructed to cancel, the event bubbles up the containment hierarchy after the event handler of the target (if an event handler is defined) executes its last statement.
  3. Events bubble all the way to the top of the containment hierarchy, unless canceled along the way.

WORKING TOGETHER

As opposite as these two systems appear, they can work quite well together if your intention is to write event handlers high up the object hierarchy that are to be shared among several related objects. Only a tiny bit of platform-specific branching is needed, because both browsers share a lot of the right syntax.

Example 1 shows a very simple document that handles all click events at the document level. Thus, all clicks on either the document or the button ultimately invoke the sayHey() function. The code at the beginning of the script is a generic routine that sets platform-flag global variables for use throughout the document. The sayHey() function is the function that is to be invoked by a click of the mouse button. Called by the onLoad event handler, the init() function turns on event capture for Communicator users, causing all click events to be directed to whatever function is referenced by the document object's onClick event handler, defined in the subsequent statement. Both browsers have the document object's onclick event handler set to call the sayHey() function. When this is run in IE4, the click event from the button bubbles up to the document object, where the event is handled.


Example 1
<HTML>
<HEAD>
<TITLE>New Window</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var isNav4, isIE4
if (parseInt(navigator.appVersion.charAt(0)) >= 4) {
   isNav4 = (navigator.appName == "Netscape") ? true : false
   isIE4 = (navigator.appName.indexOf("Microsoft" != -1)) ? true : false
}
function sayHey() {
   alert("Hey!")
}
function init() {
   if (isNav4) {
      document.captureEvents(Event.CLICK)
   }
   document.onclick = sayHey
}
</SCRIPT>
</HEAD>
<BODY onLoad="init()">
<FORM>
<INPUT TYPE="button" NAME="myButton" VALUE="Click Here">
</FORM>
</BODY>
</HTML>


Mousemove events in Communicator are not affiliated with any object. You're expected to capture this event at the window or document level, and assign a function to the object's onmousemove property. Just like the event handler assignment in the init() function of Example 1, setting the onmousemove property of a document object will be recognized by both browsers.

Example 1 obviously lacks an important feature: the ability to distinguish between a click on a button and a click on the document background. That's where event objects -- and their entirely different treatments in the two browsers -- come into play. 

EVENT OBJECTS

When an event occurs in either Communicator or Internet Explorer 4 -- whether the event is the result of a user action or a system action -- the browser generates an event (lowercase "e") object that contains myriad details about the event. For mouse events, for example, this object knows where on the page the event occurred, the object under the cursor at that instant, and which modifier keys, if any, were held down at that moment. It is not only a shared script that may need to know this information about an event. For example, if you want to guard against the entry of numbers into a text field as the user types, the script must be able to inspect each typed character and determine whether the typed character is an allowable one. This is precisely the kind of information that the event object carries with it.

Communicator's event object is automatically passed to functions invoked by event handlers that had been assigned as event properties. Therefore, with the following kind of event handler definition

document.onclick = handleClick

the function in Communicator can catch the event object as a parameter variable:

function handleClick(evt) {
   // statements
}

For event handlers defined as tag attributes, the event keyword can be passed in much the same way as you pass the this keyword:

<INPUT TYPE="button" ... onClick="handleClick(event)">

Inside the function, the properties of the event object can be examined to help the function decide how to react to the event.

IE4's event object is a different animal. It is treated as a property of the window object. In other words, there is always an event object hanging around in the window. When a user or system action fires an actual event, the event object picks up the properties of that event and holds onto them until the event handler processing that event finishes its job. At idle time, the event object intentionally loses everything it knew about the most recent event. During processing, scripts can examine the properties of this window.event object for the kind of event-specific details described earlier.

EVENT OBJECT PROPERTIES

Confounding cross-platform development a bit more is that the browsers share precious few event object property names. This means that unless you devise your own set of APIs to bridge the gap (as shown for CSS-Positioning tasks in CSS-Positioning -- The Dynamic HTML Neutral Zone), you'll have some additional platform-specific branching in your main document scripts to handle the different property names and reference syntax.

Table 2 shows the primary event object properties for each browser -- at least the ones whose functionality is identical in both browsers. Each browser has additional, platform-specific properties. Items in common are shown in boldface.


Table 2. Communicator and IE4 event object properties
Communicator Property description Internet Explorer 4
Property Values Values Property
modifiers Event 
object 
properties
Modifier keys pressed when the event occurred Boolean altKey 
ctrlKey 
shiftKey
pageX pixel count Horizontal coordinate of event in content region of browser window pixel count clientX
pageY pixel count Vertical coordinate of event in content region of browser window pixel count clientY
screenX pixel count Horizontal coordinate of event relative to entire screen pixel count screenX
screenY pixel count Vertical coordinate of event relative to entire screen pixel count screenY
target object Object that is to receive, or that fired, the event object srcElement
type event name String value of event name (e.g., "click", "mousedown", "keypress") event name type
which integer Mouse button or keyboard key code (but some code values differ with browser) integer button 
keyCode

The coordinate properties are pretty straightforward, so I'll devote two examples to presenting little laboratories you can use to experiment with the event object under different browser circumstances. While both examples use the common tag-based event handler declarations, there is no reason you cannot apply what you'll see in the examples into pages that use the cross-platform event propagation described earlier. The exact implementation depends heavily on the structure of your documents and the events you design into them.

DETECTING MODIFIER KEYS

The first event object laboratory, shown in Example 2, presents a standard link and text input field. Both the onMouseDown event handler of the link and the onKeyPress event handler of the text field invoke the same function -- a poor man's cross-platform event propagation scheme if I ever saw one. The function branches to look at the platform-specific event object and determine which of the four possible modifier keys are pressed when the event occurs. There is an extra lesson in this example, as you'll soon see. If you're using a level 4 browser, you can view Example 2.


Example 2
<HTML>
<HEAD>
<TITLE>Modifiers Keys Properties</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var isNav4, isIE4
if (parseInt(navigator.appVersion.charAt(0)) >= 4) {
   isNav4 = (navigator.appName == "Netscape") ? true : false
   isIE4 = (navigator.appName.indexOf("Microsoft" != -1)) ? true : false
}
function checkMods(evt) {
   var form = document.forms[0]
   if (isNav4) {
      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 if (isIE4) {
      form.modifier[0].checked = window.event.altKey
      form.modifier[1].checked = window.event.ctrlKey
      form.modifier[2].checked = window.event.shiftKey
      form.modifier[3].checked = false
   }
   return false
}
</SCRIPT>
</HEAD>
<BODY>
<B>Event Modifier Keys</B>
<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 onKeyPress="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> 

Both calls to the checkMods() function pass the Communicator event object. The function defines a parameter variable for the incoming object, and that variable is used in the branch for Communicator processing; it is ignored for IE4 processing. That's because IE4 references to the event object look to the window object for its event property.

Each browser also has a very different way of determining when a modifier key has been pressed in concert with an event.

  • In Communicator, the event object's modifiers property is compared (with a bitwise AND operator) against the constants held by the Event (capital "E") object (an object that works here a lot like the Math object). The Event object has a separate constant value for each of the four modifier keys. If you AND the constant against the modifiers property, you get true if the constant value is a component of the modifiers value.
  • For IE4, the event object has separate properties for each of three modifier keys (there is no property for the meta key). Each of these properties is a simple Boolean value, which allows the lab to set the checked property of the checkbox objects based on which modifier key or keys were held down during the event.

When you use this example on either browser, you'll experience some unexpected behavior in the text field. The browser does not yield its sovereignty over numerous Ctrl+key combinations or any Alt+key combinations. This may cause you to narrow your sights for defining accelerator key combinations for your pages.

BUTTON AND KEY CODES

The final example of this survey shows you how to extract information about which mouse button or which keyboard key is involved in an event. Example 3 lists another laboratory page for experimenting with these features. Results of the event object properties for this information are shown in the status bar as you click a button or type into a textarea. Like the lesson about modifier keys and text fields in Example 2, this lab holds an extra lesson. But first, let's look at the code.


Example 3
<HTML>
<HEAD>
<TITLE>Button and Key Codes</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var isNav4, isIE4
if (parseInt(navigator.appVersion.charAt(0)) >= 4) {
   isNav4 = (navigator.appName == "Netscape") ? true : false
   isIE4 = (navigator.appName.indexOf("Microsoft" != -1)) ? true : false
}
function checkWhich(evt) {
   var theKey
   if (isNav4) {
      theKey = evt.which
   } else if (isIE4) {
      if (window.event.srcElement.type == "textarea") {
         theKey = window.event.keyCode
      } else if (window.event.srcElement.type == "button") {
         theKey = window.event.button
      }
   }
   status = theKey
   return false
}
</SCRIPT>
</HEAD>
<BODY>
<B>Button and Key Codes From Event Objects</B> (results in the status bar)
<HR>
<FORM NAME="output">
<P>Click on this
<INPUT TYPE="button" VALUE="Button" onClick="checkWhich(event)">
with either mouse button (if you have more than one).</P>
<P>Enter some text with uppercase and lowercase letters:<BR>
<TEXTAREA COLS=40 ROWS=4 onKeyPress="checkWhich(event)" WRAP="virtual"></TEXTAREA></P>
</FORM>
</BODY>
</HTML>



I cheat a bit again for the purposes of the demonstration by calling one function from both the onClick event handler of the button and the onKeyPress event handler of the textarea. The function that handles the event must branch to accommodate the different ways each browser extracts button and key information.

  • For Communicator, the which property of the event object is an integer whose range of values depends on the event type. For a button, the which value is 1 for the primary button; for a keyboard key, the which value is the ASCII value of the character associated with the key. Therefore, the script extracts one value regardless of the event or object type.
  • For IE4 processing, the script examines the kind of object that originally received the event (window.event.srcElement). The type property of relevant objects reveals the kind of object receiving the user action. For a textarea, the script needs to read the keyCode property of the event object. For standard English characters, the values are the same as Communicator's which property. To get the mouse button used to click the screen button, the script reads the window.event.button property. While this value is an integer as it is in Communicator, the values are different: a click of the primary mouse button is a value of 0.

If you're using a level 4 browser, you can view Example 3.

The bonus lesson in this example is that even though the event objects are supposed to report different values for the right mouse button (if you have a multiple-button mouse), in practice the browsers don't let you trap for this user event. Right clicks in Windows 95 or NT, for example, display a context-sensitive pop-up menu, without passing the event to the page.

MOMENTOUS EVENTS

When I first learned of the different event models in the two browsers, I wondered how they could possibly be reconciled for cross-platform development. To my delight, I discovered that there was significant overlap in the implementations. And even where the approaches are opposite of each other, it isn't too difficult to devise scripts that accommodate many features of both models in one document, since the respective syntaxes scarcely step on each other.


View Source wants your feedback!
Write to us and let us know
what you think of this article.

Author and consultant Danny Goodman's twenty-fifth book is JavaScript Bible, published by IDG Books. He is currently pounding the keyboard on a new book about Dynamic HTML.

(11.97)


Related Reading


Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use

spacer spacer


                                                       
iPlanet International | Year 2000 | Site Map | Feedback
Products | Solutions | Support | Services | Download | About Us | Developer
© 2000 Sun-Netscape Alliance. All Rights Reserved  Privacy Policy