The Secret Life of Events
You saw that an event message (e.g., click) can trigger an event handler (onclick) that is attached to a DOM object. However, an event message isn’t just received by that one DOM element: Events travel up and down the DOM and are received by any elements they pass through along the way. This feature, known as event propagation, gives you some interesting options in how and where events get handled, and which elements the event affects.
In this next example, I want to highlight a table row when the cursor moves over it, and remove the highlight when the cursor moves off it. The problem, as you will see, is that although I want to highlight the table row, the mouse events that tell whether the cursor is moving in or out of a given row are in fact triggered by the table cell within the table row, or by the link within the table cell within the table row, if a link is present.
In other words, unlike the examples I’ve shown you so far, the event’s target is not the element I want to change when the event occurs. To understand how to find and modify elements that are not the target of the event, let’s first discuss the concepts of event capturing, event bubbling, and event delegation.
Capturing and Bubbling
When an event message is fired (and let’s say it’s a click event for the sake of this discussion), that click message does not go directly to the target of the event. It is first sent to the object at the top of the DOM hierarchy, body, and then moves down through the document tree to the target—the object that actually received the click. The message’s downward journey to the target element is known as the capturing phase.
Once the message reaches the target object, it then travels back up the DOM to the body tag: The upward journey is called the bubbling phase. After the message makes it all the way back up to the body tag, like a salmon swimming upstream to its spawning grounds, its life ends.
You can add an event handler to any element that receives the click message as it makes this “down-and-then-up-again” journey. This can bring great efficiencies to your code, because you can now apply a technique called event delegation.
Event delegation is the technique by which you place an event handler on an element that is not the target element of the event. The most advantageous use of event delegation is when you attach the event handler to the parent of a large number of child elements that must all provide a response to a particular event.
Significant coding economies can be realized by taking advantage of event delegation. Instead of attaching individual event handlers to every child, the message of interest is allowed to bubble up from whichever child triggered it and is handled by a single handler that is attached to the parent element. I’ll show this process in the next example. If needed, the event object can supply the name of the target element: Then just that one “downstream” element—out of the many that might have triggered the parent’s event handler—can be modified.
The W3C model supports both the capturing and bubbling phase. The third argument of a W3C event registration (highlighted)
someElement.addEventListener('focus', doHighlight, false)
is set to true if the event is to be handled in the capturing phase and false if it is to be handled in the bubbling phase.
However, the capturing phase is not supported in IE, and event delegation really only makes sense as the message travels up the DOM from children to parents. For these reasons, you will almost always use the bubbling phase to delegate events. In the W3C model, that third argument will, almost without exception, be false.
All these concepts will be illustrated as I add the rollover effect to the striped table.