Before addressing solutions to the situation describe on the previous page, let's look at how event handlers (sometimes called event listeners) are bound to events. Binding is how the software "knows" to run a particular function for an event. We bound functions to the onmouseover and onmouseout events in the examples on the previous page.

There are three ways to bind handlers to events:

in-line
This is the oldest where the code is in the HTML
<tag onmouseover="function_name(agruments);">content</tag>
Assignment to an object's property
Commonly seen as window.onload = function_name;
Using a special function
for IE
target_element.attachEvent("onmouseover", function_name);
for Mozilla/Firefox/Netscape; et al
target_element.addEventListener("mouseover", funciton_name, false).

A full explanation of these binding methods belongs in another article. Here we need to understand just a few basics. In-line binding is the only method that allows parameters, and to access the event object in Gecko based browsers one formal parameter has to be "event". We'll see this later. The other useful parameter is "this", a reference to the element, because "this" within a function bound in-line refers to the window and not the element. Functions bound with the other two methods have the element as their context. That means "this" refers to the element as needed. Also, in Gecko based browsers the event object is passed as a parameter.

Current Web design strategy attempts to separate content, display or appearance, and behavior. In-line binding goes against this strategy so you may want to give serious consideration to the other methods.

In-line Event Binding  

The first solution was good enough so why do we need an other? Well, we can reduce the amount of code, which improves maintainability, and eventually separate behavior from content. This next example will only deliver on reducing code since it uses in-line binding. This gives us an incremental increase in understanding and a foundation for a later example not using in-line binding.

The event handler is moved from each cell to the table element. We can put the onmouseover and onmouseout event handlers in the table just once and have the same affect on each cell. This can be done because events bubble up through tags.

Tested and worked on:
Netscape 7.x +, Konqueror 3.0+, Mozilla 1.3.1, Mozilla 1.7 +, Opera 7.21, FireFox , IE 5.0, IE 5.5, and IE 6.0.28.
Failed on:
Opera 6.0.2.

Try this out. Run the mouse over the table and you'll see the same changes as before. There is a catch here, in some browsers the cells flash and in all browsers cell 3.2 flashes.

Column1 Column2
Data 1.1 Data 1.2
Data 2.1 Data 2.2
Data 3.1 Data 3.2

Most likely it didn't look any different than before. But wait! What's with the cell labeled Data 3.2? As mentioned, in some browsers it flashes and in others all the cells flash. Let's look at this code first, and then we'll make the changes to deal with Data 3.2.

Take a moment to examine the changes to our JavaScript from before. The change are highlighted.

    <script type="text/javascript">
        function roll2(e, highlightcolor, textcolor){
            var obj = (typeof event != 'undefined')? event.srcElement : e.target;
            if (obj.tagName == "TD" && obj.className == "changeMe"){
                obj.style.backgroundColor = highlightcolor;
                obj.style.color = textcolor;
            }
        }//eof
    <script>

The changes from the function on the previous page are highlighted. The function roll2 gets three arguments: an event object, and the background and text colors. This is similar to before except the reference to the cell has been replaced with an event object. The new code adds these steps:

  1. Test whether we are getting the W3C object as an argument or have to used the MSIE proprietary global event object.
  2. Get a reference to the cell that triggered the event from the event object's property: srcElement for IE 1 or target for W3C compliant browsers.
  3. Test that the tag name of the element that triggered the event is TD: a cell.
  4. And, test that the class is changeMe so we only change cells we want highlighted.

Only two lines of code were added.

Browser sniffing or other tests for MS IE could be used in the function. But, whenever possible, it is best to test for the object the script needs like we tested for the event object. See Browser Detection for information.

Take a look at the HTML

            <table onMouseover="roll2(event, 'yellow', 'black');"
                    onMouseout="roll2(event, 'darkcyan','white');">
            <tr><td class="changeMe">data</td></tr>

The onMouseover and onMouseout event handlers are now in the table tag. The TDs no longer have the event handlers, but the cells that should respond have the class changeMe. The class lets us choose to change some cells and not others.

What's Happening?

When the mouse triggers an event it bubbles up from the element where the event was triggered through all element tags in its ancestry to the first tag that has a handler. The handler, function roll2, assigned to the onMouseOver and onMouseOut events on the table tag process the event. In W3C DOM compatible browsers the handlers are passed the event object and the new colors. In IE the event argument is null, and the global handler is used.

The code is still portable; albeit, it doesn't work in some older browsers, as demonstrated by Opera 6.0.2.

What About Data 3.2? — Refining Event Handling  

I said I'd get back to that, didn't I? Well, try this.

Column1 Column2
Data 1.1 Data 1.2
Data 2.1 Data 2.2
Data 3.1 Data 3.2

That works better. The event bubbles up (that's the technical term) from the lowest element through to the TABLE tag where the event handler captures the rollover. Data 3.2 has an italic font style set by using opening and closing tags, <i></i>, within the TD tag. So when the mouse moved between the italic tags it, in effect, move out of the TD tag. The target (or srcElement for IE) is i not td. That's not what we want. Look at the code changes.

    function roll3(e,highlightcolor, textcolor){
        var obj = (typeof event != 'undefined')? event.srcElement : e.target;
        var is_w3cdom = (typeof obj.parentNode != "undefined") ? true : false;
        while(obj.tagName!="TD" && obj.tagName!="TABLE"){
                obj= is_w3cdom? obj.parentNode : obj.parentElement;
        }
        if (obj.tagName == "TD" && obj.className == "changeMe") {
                obj.style.backgroundColor = highlightcolor;
                obj.style.color = textcolor;
        }
    }//eof

The new code performs the follow additional steps:

  1. Determines how to access the parent. If the browser has the W3C parentNode property, the code uses that; otherwise it uses the IE parentElement property.
  2. Steps through the tags starting with the one that triggered the event up the parent tags. If we come upon a TD tag, it's dealt with as before.

The check for the TABLE tag is a safety measure. It stops the loop when it is clear a cell is not in the event chain.

Binding the Handler to an Object Property  

We have all the tools needed to use binding methods other than in-line so here goes.

Column1 Column2
Data 1.1 Data 1.2
Data 2.1 Data 2.2
Data 3.1 Data 3.2
    function roll3_5(e){
        e = (typeof event != 'undefined')? event : e;
        var obj = (e.target)? e.target : e.srcElement;
        var is_dom = (typeof obj.parentNode != "undefined") ? true : false;
        while(obj.tagName!="TD" && obj.tagName!="HTML"){
                obj= is_dom? obj.parentNode : obj.parentElement;
        }
        if (obj.tagName == "TD" && obj.className == "changeMe") {
            switch (e.type) {
            case "mouseover":
                obj.style.backgroundColor = "#ff0";
                obj.style.color = "#000";
                break;
            case "mouseout":
                obj.style.backgroundColor = "";
                obj.style.color = "";
                break;
            }
        }
    }//eof
    window.onload = function () {
        document.getElementById("binding").onmouseover = roll3_5;
        document.getElementById("binding").onmouseout = roll3_5;
        };

There is some new code. The first two new or changed lines aren't really significant. They are there to allow the later use of e.type. What is significant is the switch statement. Since parameters cannot be passed, the event type value causes branching for the required background and text colors, which are hard coded in the function.

The second thing to notice is the use of the window.onload event to bind the function roll3_5 to the table after it has been created. An anonymous function was used to bind the binding script to the onload event.