19.3 The Internet Explorer Event ModelThe 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 ObjectLike 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:
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 VariableAlthough 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 IEThe 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 RegistrationIn 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 ModelExample 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 } } |