- Techniques to Handle Events
- The First Event: load
- The Event Object
- The Secret Life of Events
- Striped Table with Rollovers
- The Up and Down Life of Keys
- Summary
Striped Table with Rollovers
In Chapter 3, I showed you how to add stripes to the rows of a table. You can see the complete JavaScript code on page 86. Now I’ll modify this code so that each table row highlights as the cursor moves over it.
I’ll start by modifying the HTML markup in two places. I’ll first add a link into one cell of the table.
<td><a href=http://ajaxinaction.com>Ajax in Action</a></td>
It’s very common to have links and other elements in a table cell, and the code needs to be robust enough to cope with such circumstances. This one link will represent an additional level of content hierarchy within the tds while I test.
Using an Element as a Debugging Tool
Next, I’ll manually add a p tag with the attribute id=”display” right after the table.
</table> <p id="display">Display Area</p> </body>
This element will display the kinds of test values that I have displayed with alert dialogs in earlier examples. When I start testing these rollovers, events will be generated so rapidly that I would soon tire of clicking the OKs in all those alert dialogs. Instead, I’ll write my test results into the text of this element, which currently reads “Display Area”, and see the results right in the page—no alerts needed. Now, any time I want to check a value, I can use this line of code
document.getElementById("display").innerHTML = valueToDisplay
and the words “Display Area” will be replaced by the value of valueToDisplay. Just remember, whenever you see
document.getElementById("display").innerHTML = someValue
in subsequent code, you’ll know I am just displaying a test value.
The final preparatory step is to add a CSS style that will be the highlight background color for the rows—in this case, white.
The sole purpose of the code I’ll write is to add and remove the stripe_table class name from the table rows as the cursor passes over them.
Mouse Events
JavaScript is very chatty when it comes to mouse events. Every time the mouse moves on or off an element, or even moves the smallest amount, events are fired off describing these activities.
To detect if the cursor is over a table row, I’ll use the mouse events mouseover and mouseout, which occur when the mouse enters and leaves an element, respectively.
Event Delegation
Because events bubble up, I’ll add the two event listeners for these events at the tbody level. I want only table rows within the body of the table to highlight, so tbody is the highest level at which I can detect them without also receiving events from the table rows that are in the header, which I don’t want to stripe.
I am already getting the tbody element in the existing striping code, so it’s easy to add event handlers in the line right after that one.
Code 4.13. table_stripe_roll1.html
In the first piece of highlighted code, I again use the addEvent helper function to add the event listeners, specifying that the mouseover and mouseout events will be attached to the tbody element. Both of these events will call the same function, checkEventSource.
As its name suggests, the checkEventSource function will check which element was the source of the event when the mouse moves over the table rows and then add the hilite class to the related table row. That functionality will come later. For now, as you can see in the second piece of highlighted code, I’ve added the checkEventSource function and within it simply used the “Display Area” element to display a bit of text confirming that the function was called.
When I first load the page, I see the “Display Area” default text in the lower-left corner, as shown in Figure 4.10.
Figure 4.10 The page with the default Display Area text.
When I move the cursor over the bottom row of the table, as shown in Figure 4.11, the checkEventSource function is called and the “Display Area” message is updated.
Figure 4.11 The Display Area text is replaced with the text from the function.
This confirms that at least one of the two event handlers, mouseover, is attached successfully. I can be fairly confident that mouseout was too—I’ll soon find out.
From the perspective of this code, I know that the mouseover event is bubbling up to the event listener on the tbody, but I don’t know the triggering element, so I don’t yet know which row I need to stripe.
Determining the Target Element
The next step is to determine the target element—the element that is actually triggering the event. To do this, I need to get the target element’s nodeName, which in the case of an element gives the name of the tag—a, td, or tr, for example.
Code 4.14. table_stripe_roll2.html
checkEventSource:function(e) { var evt = e || window.event; var evtTarget = evt.target || evt.srcElement; document.getElementById("display").innerHTML = evtTarget.nodeName; }
You’ve seen the first two lines of code within this function before—they get the event target. The highlighted code in the third line simply gets the node name of that target element; you can see the node names displayed in the lower-left corners of Figures 4.12 and 4.13.
Figure 4.12 When the cursor is over a table row, the event target is a table cell.
Figure 4.13 When the cursor is over a link within a table row, the event target is the link.
This test shows that the table row elements don’t trigger events when the mouse goes over them, because they are entirely filled with their child tds. There simply is nowhere you can place the cursor to get the tr to trigger a mouse event. The target element—the element directly under the mouse—will always be the td unless you explicitly add padding or margins to create space between it and the tr. The td, because it’s a child of the tr, will always be on top of the tr in the element stacking order and will therefore always be the target of any mouse event. You can also see in Figure 4.14 that if the mouse moves over a link within a td, as you might expect, that link will be the target.
Figure 4.14 The event bubbles up to the event listener on tbody.
The most important thing that this test shows is that the target of the event, be it the td, a link, or some other element that might appear within a table cell, is a child of the row that you want to highlight.
Traversing the DOM
To find the event target’s ancestor table row so I can apply the row_hilite CSS class to it, all I need to do is start moving up the DOM from the target element until I find an element with the node name tr and apply the highlight class to that element. This process of moving up, down, or across the DOM to an adjacent element is known as traversing.
Code 4.15. table_stripe_roll3.html
You can see the way I do this in the preceding highlighted code. I simply change the event target property from the target element to the parent of that element. This effectively moves me up the DOM—if the target was a, a link, I am now at the level of its parent table cell. I keep moving up like this until my test returns tr. (Note that I use the String method toLowerCase to convert the node name to lowercase for cross-browser compatibility. Some browsers, such as Firefox, return the node name in uppercase, as you can see in the lower-left corners of Figures 4.14 and 4.15.)
Figure 4.15 Even though the cursor is over a link, the reported event target is now the table row.
By the time I get a tr and the loop stops, my event target is already set to tr because each time the loop runs, I set the target to the parent element of the current target element before the loop determines if it should run again. The event target is now set to the element I want to modify and is no longer set to the original target of the event. In short, now when the mouse rolls over the table, the target element, whether a link or a table cell (or anything else), is instantly changed to the tr ancestor of that target element, as illustrated in Figure 4.15.
Adding the Highlight
Now that I have the row I want to highlight as the reported target for the mouseover and mouseout events, a few lines of code within a switch statement are all that’s needed to add and remove the hilite class.
Code 4.16. table_stripe_roll4.html
Figure 4.16 shows that the row that has the cursor over it now highlights, and the rollover effect code is complete.
Figure 4.16 Each row now highlights when the cursor is over it.
This is an good example of event delegation, because three separate elements are involved. The event happens to the link, the event handler attached to the table head responds to the event, and the link’s table row is modified. This relationship is illustrated in Figure 4.17.
Figure 4.17 The checkEventSource function determines the parent table row of the event target.
You can see in the code that before I set the row_hilite class on the tr, I store any class that is currently on that element. This is because there is a 50 percent chance that the row already has the odd class that does the striping. So, if the event handler is mouseover, which means I need to add the row_hilite class, I put any existing class name in a variable called oldClass and then change the class on the element to row_hilite. When the user moves off the row and the mouseout handler fires, I set that row back to the class name in the oldClass variable.
As illustrated in the previous diagram, what is really interesting about this example is that the element with the event listener is tbody, the element that triggers the event is td or a, and the element that is modified as a result of the event is tr. By combining event listeners, event delegation, event bubbling, and DOM traversing, you can have a single event handler manage events from dozens, hundreds, or thousands of child elements and then have the event affect any element that you choose.
Now that you’ve seen some ways to work with mouse events, let’s take a look at the other way the user interacts with your application—the keyboard.