Jump to content

How to equalize the IE and W3C event models

0
  dannyg1's Photo
Posted Sep 02 2009 09:00 AM

Because Internet Explorer (at least through version 7) does not implement the W3C DOM event model, and because pure W3C DOM browsers such as Mozilla and Safari implement only the W3C DOM event model, you must use model-specific ways to derive a common reference to the event to assist further processing.

Event handlers receive a reference to the W3C DOM event object as a parameter (see the Discussion), whereas the IE event object is a property of the window object. To accommodate both and end up with a single reference that subsequent statements can use to examine the event object, use the following skeletal structure in every event-invoked function:

    function functionName(evt) {

        evt = (evt) ? evt : ((window.event) ? event : null);

        if (evt) {

            // perform processing here

        }

    }

This structure uses object detection, rather than browser version sniffing, to not only extract the event model-specific reference, but also guard against script errors if the browser does not support either event model (i.e., much older browsers responding to simple events).

If your script structure prevents older browsers from invoking event handler functions (e.g., you use object detection to make sure only newer browsers bind events to elements), the first statement of the handler function can use shortcut notation—via the OR (||) Javascript operator—to assign the browser-supported event object to the evt variable, as follows:

    function functionName(evt) {

        evt = (evt) || window.event;

        // perform processing here

    }

You will see this notation used in several scripts later in this book.

Getting a non-IE event model browser to pass the event object to the event handler function requires either a little planning ahead or none whatsoever, depending on the way you bind your event handlers to element objects. The W3C DOM specification formally supports event binding only via the addEventListener() method of any element, as in the following example:

    document.getElementById("myButton").addEventListener("click", processClick, false);

A function triggered by an event bound to the element in this fashion automatically passes the event object as the sole parameter to the function. All you need to do is catch the parameter by assigning a function parameter variable at the start of the function definition (i.e., the evt inside parentheses in the skeletal structure shown in the Solution).

IE's preferred event binding is through the attachEvent() method. Its syntax, however, is different from that of the W3C's addEventListener() method. In a moment, you'll see how to reconcile the differences.

Fortunately for cross-platform developers, two pre-W3C DOM ways of binding events to elements work without a problem in all modern browsers—and will likely do so for a long time to come. Those two approaches are assigned as element object properties, and assigned the original way, as attributes inside the element's tag.

Assigning an event as a property of an object is performed in one Javascript statement. The left side of the statement is a reference to the element object and a property consisting of the event handler name (with the "on" prefix) in lowercase letters; the right side is a reference to the function to be invoked when that element receives the event. A function reference is just the function's name without parentheses, as in:

    document.getElementById("myButton").onclick = processClick;

Events bound this way in browsers such as Mozilla automatically pass the event object as the sole parameter to the function.

Although at first the event property binding technique appears to preclude passing parameters, you can use an anonymous function to fill the gap. The following example passes both the W3C DOM event object and a timestamp of the event to the processClick() function:

    document.getElementById("myButton").onclick = function(evt) {processClick(new Date(),

evt);};

You can pass any literal values or variables within the scope of the assignment statement (e.g., a local variable if this statement appears inside a function).

Despite the number of competing binding techniques, save yourself a lot of anguish by combining three techniques (W3C standard, IE proprietary, and event properties) into a library, such as eventsManager.js. Two of the library's three functions are as follows:

    function addEvent(elem, evtType, func, capture) {

       capture = (capture) ? capture : false;

       if (elem.addEventListener) {

          elem.addEventListener(evtType, func, capture);

       } else if (elem.attachEvent) {

          elem.attachEvent("on" + evtType, func);

       } else {

          // for IE/Mac, NN4, and older

          elem["on" + evtType] = func;

       }

    }



    function removeEvent(elem, evtType, func, capture) {

       capture = (capture) ? capture : false;

       if (elem.removeEventListener) {

          elem.removeEventListener(evtType, func, capture);

       } else if (elem.attachEvent) {

          elem.detachEvent("on" + evtType, func);

       } else {

          // for IE/Mac, NN4, and older

          elem["on" + evtType] = null;

       }

    }

These two functions offer a simple interface for your code to bind and unbind events to element objects. For example, the button event example used so far would be handled by the following:

    addEvent(document.getElementById("myButton"), "click", processClick, false);

The library applies the preferred binding syntax to each browser class, including a version for older browsers.

One other cross-platform (and fully backward-compatible) event binding mechanism is through an attribute embedded in the element's tag. You see a lot of this in code around the Internet because it was the original way event binding took place when scripting started. It is also a convenient way to pass one or more parameters of your choosing to the function. Therefore, it's common to see input form controls pass a reference to the element itself (this) or the containing form element (this.form) to make it easier for the function to work directly with the element's value or other controls in the form. But to convey the W3C DOM event object to the handler function, events bound this way must also explicitly include the keyword event as one of the parameters:

    

You can pass multiple parameters if you like, but the position of the parameters being passed must be the same in the parameter variable definitions in the function. For example, the event handler defined as:

    onclick="processClick(this.form, event)"

must have a function defined with two parameter variables to catch the passed arguments:

    function processClick(form, evt) {...}

When you include the explicit event object as a parameter, IE also passes its version of the event object, so the syntax does double duty. But the trend in scripting and document markup in general is to get away from this in-tag event binding and gravitate toward other approaches exemplified by the eventsManager.js library, earlier—all in the name of separating content from style and processing.

Binding events by a process other than in-tag attributes, however, has its own gotchas to be aware of. The most significant is that you cannot simply bind events to objects while the page loads but before the objects are loaded into the browser. The Javascript interpreter attempts to resolve all assignment statements when they execute. If the object is not yet known to the object model, the assignment fails (with a script error). This goes even for the body element: assigning an event handler property to document.body in the head portion of the document triggers script errors in some browsers.

To work around this conundrum, you can place (almost) all of your event assignments in a function that gets invoked after the entire page has loaded.

If you use the event object equalization routine shown in the Solution, you may still have to perform additional platform-specific equalizations in your handler function. This occurs whenever the property you're looking for is known by different names. But there is also a sufficient amount of similarity between the two objects' properties that they are quite useful blended into a single event handler function.

Javascript & DHTML Cookbook

Learn more about this topic from Javascript & DHTML Cookbook, 2nd Edition.

In today's Web 2.0 world, Javascript and Dynamic HTML are at the center of the hot new approach to designing highly interactive pages on the client side. With this environment in mind, the new edition of this book offers bite-sized solutions to very specific scripting problems that web developers commonly face. Each recipe includes a focused piece of code that you can insert right into your application.

See what you'll learn


Tags:
0 Subscribe


0 Replies