Sunday, June 10, 2007

Events, Event Listeners and Event Propagation Model

Firefox version: 2.0.0.4 (Mac)

Adding event listeners: the good and the bad
Typical syntax for adding listeners to page elements looks something like this:

element.onclick = myEventListener;

where onclick is an attribute and myEventListener is a name of a function that takes zero or one arguments and is called when a particular event is triggered at element. This is a common and convenient syntax for JavaScript embedded in the page but it has one major and important drawback for extension writers: adding an event listener this way causes all previous listeners registered at that element for that type of an event to be de-registered. Therefore a better choice is the addEventListener method, which allows multiple listeners to be notified of the same event type:

void addEventListener(in DOMString type,
in EventListener listener,
in boolean useCapture);

Where
type is a string specifying the name of the event type (e.g., “mousedown”)
listener is name of a function taking one parameter of type Event
useCapture specifies whether events addressed to the descendants of this element should be captured by this listener first, before they reach their target (I will talk about capturing a little bit more below)

Event propagation model
The event propagation model in JavaScript is somewhat unusual and deserves some attention. Any event can go through up to three phases: capturing, at target and bubbling (see the illustration below). Let us assume that a mouseover event is triggered by a link element. That event first travels through all of the ancestors of the link element, starting with the document element itself. This is the capturing phase and only event listeners registered with useCapture = true are triggered during that phase. Subsequently, the event reaches its target and is now in the at target phase. Finally, some (but not all) events will “bubble” upwards through the DOM tree all the way up to the document again. During this bubbling phase only events registered with useCapture = false will be triggered.
illustration of how JavaScripts events propagate through the DOM

There are a number of methods for altering the propagation path of an event:

- During capturing phase, a call to event.stopPropagation() method will prevent the further propagation of the even although all other event listeners registered at the same level in the hierarchy will still receive the event;

- During at target or bubbling phase, a call to event.stopPropagation() method will prevent the further bubbling of the event, so no listeners registered higher up in the hierarchy will receive the event;

- During capturing or at target phases, one may call event.preventDefault() to prevent the event from triggering the default action in the browser (for example, to stop the browser from following a link in response to a mouse click). This only applies to events that are designated as cancelable.

Example: highlighting page elements on mouse-over
Note that the JavaScript event propagation model makes it possible to register just a single event listener at any point in the DOM hierarchy to receive all events of a particular type from all of the descendants of that element. The example below shows how to change the background color of a page element (a leaf or an intermediate node) on a mouse-over event.

var originalColors = {};

var HelloWorld = {

...

instrumentCurrentPage:
function() {
window.content.document.addEventListener("mouseover", this.handleMouseOverEvent, false);
window.content.document.addEventListener("mouseout", this.handleMouseOutEvent, false);
},

handleMouseOverEvent:
function(event) {
originalColors[event.target] = event.target.style.background;
event.target.style.background =
"#f00";
},

handleMouseOutEvent:
function(event) {
event.target.style.background = originalColors[event.target];
},
}


The originalColors variable is used to store the original background colors of elements just before the highlight is applied, so that the handleMouseOutEvent can restore the original background when the mouse is moved away.

In this case, the event handlers are triggered during bubbling phase but the same effect is achieved if useCapture were set to true.

The screen shot below shows this extension script in action.
a screen shot showing a web page element being highlighted on mouse-over


Resources
I learned the most from a W3C document on Document Object Model Events.

0 Comments:

Post a Comment

<< Home