JavaScript: The Definitive GuideJavaScript: The Definitive GuideSearch this book

19.3. The Internet Explorer Event Model

The event model supported by Internet Explorer 4, 5, 5.5, and 6 is an intermediate model, halfway between the original Level 0 model and the standard DOM Level 2 model. The IE event model includes an Event object that provides details about events that occur. Instead of being passed to event handler functions, however, the Event object is made available as a property of the Window object. The IE model supports event propagation by bubbling, but not by capturing, as the DOM model does. In IE 4, event handlers are registered in the same way as they are in the original Level 0 model. In IE 5 and later, however, multiple handlers may be registered with special (but nonstandard) registration functions.

The following sections provide more detail about this event model and document it by comparison to the original Level 0 event model and the standard Level 2 event model. Therefore, you should be sure you understand those two event models before reading about the IE model.

19.3.1. The IE Event Object

Like the standard DOM Level 2 event model, the IE event model provides details about each event that occurs in the properties of an Event object. The Event objects defined in the standard model were in fact modeled on the IE Event object, so you'll notice a number of similarities between the properties of the IE Event object and the properties of the DOM Event, UIEvent, and MouseEvent objects.

The most important properties of the IE Event object are:

type
A string that specifies the type of event that occurred. The value of this property is the name of the event handler with the leading "on" removed (e.g., "click" or "mouseover"). Compatible with the type property of the DOM Event object.

srcElement
The document element on which the event occurred. Comparable to the target property of the DOM Event object.

button
An integer that specifies the mouse button that was pressed. A value of 1 indicates the left button, 2 indicates the right button, and 4 indicates the middle button. If multiple buttons are pressed, these values are added together -- the left and right buttons together produce a value of 3, for example. Compare this with the button property of the DOM Level 2 MouseEvent object, but note that although the property names are the same, the interpretation of the property values differs.

clientX , clientY
These integer properties specify the mouse coordinates at the time of the event, relative to the upper-left corner of the containing window. Note that for documents that are larger than the window, these coordinates are not the same as the position within the document, and you may want to add the values document.body.scrollLeft and document.body.scrollTop, respectively, to account for scrolling. These properties are compatible with the DOM Level 2 MouseEvent properties of the same name.

offsetX, offsetY
These integer properties specify the position of the mouse pointer relative to the source element. They enable you to determine which pixel of an Image object was clicked on, for example. These properties have no equivalent in the DOM Level 2 event model.

altKey , ctrlKey, shiftKey
These boolean properties specify whether the Alt, Ctrl, and Shift keys were held down when the event occurred. These properties are compatible with the properties of the same name in the DOM Level 2 MouseEvent object. Note, however, that the IE Event object does not have a metaKey property.

keyCode
This integer property specifies the key code for keydown and keyup events and the Unicode character code for keypress events. Use String.fromCharCode( ) to convert character codes to strings. The DOM Level 2 event model does not standardize key events (although DOM Level 3 is working on this) and has no equivalent to these properties.

fromElement , toElement
fromElement specifies the document element that the mouse used to be over for mouseover events. toElement specifies the document element that the mouse has moved to for mouseout events. Comparable to the relatedTarget property of the DOM Level 2 MouseEvent object.

cancelBubble
A boolean property that, when set to true, prevents the current event from bubbling any further up the element containment hierarchy. Comparable to the stopPropagation( ) method of the DOM Level 2 Event object.

returnValue
A boolean property that can be set to false to prevent the browser from performing the default action associated with the event. This is an alternative to the traditional technique of returning false from the event handler. Comparable to the preventDefault( ) method of the DOM Level 2 Event object.

You can find complete documentation for the IE Event object in the client-side reference section of this book.

19.3.2. The IE Event Object as a Global Variable

Although the IE event model provides event details in an Event object, it never passes Event objects as arguments to event handlers. Instead, it makes the Event object available as the event property of the global Window object. This means that an event handling function in IE can refer to the Event object as window.event or simply as event. Although it seems strange to use a global variable where a function argument would do, the IE scheme works because it is implicit in the event-driven programming model that only one event is ever being processed at a time. Since two events are never handled concurrently, it is safe to use a global variable to store details on the event that is currently being processed.

The fact that the Event object is a global variable is incompatible with the standard DOM Level 2 event model, but there is a one-line workaround. If you want to write an event handler function that works with either event model, write the function so that it expects an argument, and then, if no argument is passed, initialize the argument from the global variable. For example:

function portableEventHandler(e) {
    if (!e) e = window.event;  // Get event details for IE

    // Body of the event handler goes here
}

19.3.3. Event Bubbling in IE

The IE event model does not have any notion of event capturing, as the DOM Level 2 model does. However, events do bubble up through the containment hierarchy in the IE model, just as they do in the Level 2 model. As with the Level 2 model, event bubbling applies only to raw or input events (primarily mouse and keyboard events), not to higher-level semantic events. The primary difference between event bubbling in the IE and DOM Level 2 event models is the way that you stop bubbling. The IE Event object does not have a stopPropagation( ) method, as the DOM Event object does. To prevent an event from bubbling or stop it from bubbling any further up the containment hierarchy, an IE event handler must set the cancelBubble property of the Event object to true:

window.event.cancelBubble = true; 

Note that setting cancelBubble applies only to the current event. When a new event is generated, a new Event object is assigned to window.event, and cancelBubble is restored to its default value of false.

19.3.4. IE Event-Handler Registration

In IE 4, event handlers are registered in the same way they are in the original Level 0 event model: by specifying them as HTML attributes or assigning functions to the event handler properties of document elements. The only difference is that IE 4 allows access to (and event-handler registration on) all of the elements in a document, instead of just the form, image, and link elements that are accessible with the Level 0 DOM.

IE 5 and later introduce the attachEvent( ) and detachEvent( ) methods, which provide a way to register more than one handler function for a given event type on a given object. These methods work like addEventListener( ) and removeEventListener( ), except that since the IE event model does not support event capturing, they expect only two arguments: the event type and the handler function. Also, unlike with the Level 2 event model, the event handler names passed to the IE method should include the "on" prefix: use "onclick" instead of just "click". You can use attachEvent( ) to register an event handler as follows:

function highlight( ) { /* Event-handler code goes here */ }
document.getElementById("myelt").attachEvent("onmouseover", highlight);

Another difference between attachEvent( ) and addEventListener( ) is that functions registered with attachEvent( ) are invoked as global functions, rather than as methods of the document element on which the event occurred. That is, when an event handler registered with attachEvent( ) executes, the this keyword refers to the Window object, not to the event's target element.

19.3.5. Example: Dragging with the IE Event Model

Example 19-3 is a modified version of the beginDrag( ) function that was presented in Example 19-2. This version includes code that makes it work with the IE event model, in addition to the DOM Level 2 event model. The design and intended usage of this version of beginDrag( ) are the same as in Example 19-2, so if you understood that example, you should have no trouble understanding this one. What makes this example interesting is that it juxtaposes two event models, clearly highlighting their differences.

The biggest difference in the IE version of the code is that it must rely on event bubbling rather than event capturing. This usually works, but it is not the ideal solution for this problem. Another important difference to note is that IE event handlers are not passed an Event object. Note that the code in this example also distinguishes between IE 5 and later, which support attachEvent( ), and IE 4, which does not. See the discussion of Example 19-2 for a sample HTML document that is designed to use this beginDrag( ) function.

Example 19-3. Dragging with the IE event model

/**
 * PortableDrag.js:
 * beginDrag( ) is designed to be called from an onmousedown event handler.
 * elementToDrag may be the element that received the mousedown event, or it
 * may be some containing element. event must be the Event object for the
 * mousedown event. This implementation works with both the DOM Level 2
 * event model and the IE event model.
 **/
function beginDrag(elementToDrag, event) {
    // Compute the distance between the upper-left corner of the element
    // and the mouse-click. The moveHandler function below needs these values.
    var deltaX = event.clientX - parseInt(elementToDrag.style.left);
    var deltaY = event.clientY - parseInt(elementToDrag.style.top);

    // Register the event handlers that will respond to the mousemove events
    // and the mouseup event that follow this mousedown event.  
    if (document.addEventListener) {  // DOM Level 2 Event Model
        // Register capturing event handlers
        document.addEventListener("mousemove", moveHandler, true);
        document.addEventListener("mouseup", upHandler, true);
    }
    else if (document.attachEvent) {  // IE 5+ Event Model
        // In the IE event model, we can't capture events, so these handlers
        // are triggered only if the event bubbles up to them.
        // This assumes that there aren't any intervening elements that
        // handle the events and stop them from bubbling.
        document.attachEvent("onmousemove", moveHandler);
        document.attachEvent("onmouseup", upHandler);
    }
    else {  // IE 4 Event Model
        // In IE 4 we can't use attachEvent( ), so assign the event handlers
        // directly after storing any previously assigned handlers, so they 
        // can be restored. Note that this also relies on event bubbling.
        var oldmovehandler = document.onmousemove;
        var olduphandler = document.onmouseup;
        document.onmousemove = moveHandler;
        document.onmouseup = upHandler;
    }

    // We've handled this event. Don't let anybody else see it.  
    if (event.stopPropagation) event.stopPropagation( );  // DOM Level 2
    else event.cancelBubble = true;                      // IE

    // Now prevent any default action.
    if (event.preventDefault) event.preventDefault( );   // DOM Level 2
    else event.returnValue = false;                     // IE

    /**
     * This is the handler that captures mousemove events when an element
     * is being dragged. It is responsible for moving the element.
     **/
    function moveHandler(e) {
        if (!e) e = window.event;  // IE Event Model

        // Move the element to the current mouse position, adjusted as
        // necessary by the offset of the initial mouse-click.
        elementToDrag.style.left = (e.clientX - deltaX) + "px";
        elementToDrag.style.top = (e.clientY - deltaY) + "px";

        // And don't let anyone else see this event.
        if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
        else e.cancelBubble = true;                  // IE
    }

    /**
     * This is the handler that captures the final mouseup event that
     * occurs at the end of a drag.
     **/
    function upHandler(e) {
        if (!e) e = window.event;  // IE Event Model

        // Unregister the capturing event handlers.
        if (document.removeEventListener) {  // DOM Event Model
            document.removeEventListener("mouseup", upHandler, true);
            document.removeEventListener("mousemove", moveHandler, true);
        }
        else if (document.detachEvent) {  // IE 5+ Event Model
            document.detachEvent("onmouseup", upHandler);
            document.detachEvent("onmousemove", moveHandler);
        }
        else {  // IE 4 Event Model
            document.onmouseup = olduphandler;
            document.onmousemove = oldmovehandler;
        }

        // And don't let the event propagate any further.
        if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
        else e.cancelBubble = true;                  // IE
    }
}



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.