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:
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.
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.
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:
changeMeso 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.
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.
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:
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.
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.