Section 17.2. Advanced Event Handling with DOM Level 2


17.2. Advanced Event Handling with DOM Level 2

The event-handling techniques presented so far in this chapter are part of DOM Level 0, the de facto standard API that is supported by every JavaScript-enabled browser. DOM Level 2 defines an advanced event-handling API that is significantly different (and quite a bit more powerful) than the Level 0 API. The Level 2 standard does not incorporate the existing API into the standard DOM, but there is no danger of the Level 0 API being dropped. For basic event-handling tasks, you should feel free to continue to use the simple API.

The DOM Level 2 event model is supported by all modern browsers except Internet Explorer.

17.2.1. Event Propagation

In the Level 0 event model, the browser dispatches events to the document elements on which they occur. If that object has an appropriate event handler, that handler is run. There is nothing more to it. The situation is more complex in DOM Level 2. In this advanced event model, when an event occurs on a document element (known as the event target), the target's event handler or handlers are triggered, but in addition, each of the target's ancestor elements has one or two opportunities to handle that event. Event propagation proceeds in three phases. First, during the capturing phase, events propagate from the Document object down through the document tree to the target node. If any of the ancestors of the target (but not the target itself ) has a specially registered capturing event handler, those handlers are run during this phase of event propagation. (You'll see how both regular and capturing event handlers are registered shortly.)

The next phase of event propagation occurs at the target node itself: any appropriate event handlers registered directly on the target are run. This is akin to the kind of event handling provided by the Level 0 event model.

The third phase of event propagation is the bubbling phase, in which the event propagates or bubbles back up the document hierarchy from the target element up to the Document object. Although all events are subject to the capturing phase of event propagation, not all types of events bubble: for example, it does not make sense for a submit event to propagate up the document beyond the <form> element to which it is directed. On the other hand, generic events such as mousedown events can be of interest to any element in the document, so they do bubble up through the document hierarchy, triggering any appropriate event handlers on each of the ancestors of the target element. In general, raw input events bubble while higher-level semantic events do not. (See Table 17-3 later in this chapter for a definitive list of which events bubble and which do not.)

During event propagation, it is possible for any event handler to stop further propagation of the event by calling the stopPropagation( ) method of the Event object that represents the event. The Event object and its stopPropagation( ) method are discussed further later in this chapter.

Some events cause an associated default action to be performed by the web browser. For example, when a click event occurs on an <a> tag, the browser's default action is to follow the hyperlink. Default actions like these are performed only after all three phases of event propagation complete, and any of the handlers invoked during event propagation can prevent the default action from occurring by calling the preventDefault( ) method of the Event object.

Although this kind of event propagation may seem convoluted, it can help you centralize your event-handling code. DOM Level 1 exposes all document elements and allows events (such as mouseover events) to occur on any of those elements. This means that there are many, many more places for event handlers to be registered than there were with the old Level 0 event model. Suppose you want to trigger an event handler whenever the user moves the mouse over a <p> element in your document. Instead of registering an onmouseover event handler for each <p> tag, you can instead register a single event handler on the Document object and handle these events during either the capturing or bubbling phase of event propagation.

There is one other important detail about event propagation. In the Level 0 model, you can register only a single event handler for a particular type of event for a particular object. In the Level 2 model, however, you can register any number of handler functions for a particular event type on a particular object. This applies also to ancestors of an event target whose handler function or functions are invoked during the capturing or bubbling phases of event propagation.

17.2.2. Event Handler Registration

In the Level 0 API, you register an event handler by setting an attribute in your HTML or an object property in your JavaScript code. In the Level 2 event model, you register an event handler for a particular element by calling the addEventListener( ) method of that object. (The DOM standard uses the term listener in its API, but I'll continue to use the synonymous word handler in this discussion.) This method takes three arguments. The first is the name of the event type for which the handler is being registered. The event type should be a string that contains the lowercase name of the HTML handler attribute, with the leading "on" removed. Thus, if you use an onmousedown HTML attribute or onmousedown property in the Level 0 model, you'll use the string "mousedown" in the Level 2 event model.

The second argument to addEventListener( ) is the handler (or listener) function that should be invoked when the specified type of event occurs. When your function is invoked, it is passed an Event object as its only argument. This object contains details about the event (such as which mouse button was pressed) and defines methods such as stopPropagation( ). The Event interface and its subinterfaces are discussed further later in this chapter.

The final argument to addEventListener( ) is a boolean value. If true, the specified event handler captures events during the capturing phase of event propagation. If the argument is false, the event handler is a normal event handler and is triggered when the event occurs directly on the object or on a descendant of the element and subsequently bubbles up to the element.

For example, you might use addEventListener( ) as follows to register a handler for submit events on a <form> element:

 document.myform.addEventListener("submit",                                  function(e) {return validate(e.target); }                                  false); 

If you wanted to capture all mousedown events that occur within a particular named <div> element, you might use addEventListener( ) like this:

 var mydiv = document.getElementById("mydiv"); mydiv.addEventListener("mousedown", handleMouseDown, true); 

Note that these examples assume that you've defined functions named validate( ) and handleMouseDown( ) elsewhere in your JavaScript code.

Event handlers registered with addEventListener( ) are executed in the scope in which they are defined. They are not invoked with the augmented scope chain described in Section 17.1.6.

Because event handlers are registered in the Level 2 model by invoking a method rather than by setting an attribute or property, you can register more than one event handler for a given type of event on a given object. If you call addEventListener( ) multiple times to register more than one handler function for the same event type on the same object, all the functions you've registered are invoked when an event of that type occurs on (or bubbles up to, or is captured by) that object. It is important to understand that the DOM standard makes no guarantees about the order in which the handler functions of a single object are invoked, so you should not rely on them being called in the order in which you registered them. Also note that if you register the same handler function more than once on the same element, all registrations after the first are ignored.

Why would you want to have more than one handler function for the same event on the same object? This can be quite useful for modularizing your software. Suppose, for example, that you've written a reusable module of JavaScript code that uses mouseover events on images to perform image rollovers. Now suppose that you have another module that wants to use the same mouseover events to display additional information about the image in a DHTML pop up or tool tip. With the Level 0 API, you'd have to merge your two modules into one so that they could share the single onmouseover property of the Image object. With the Level 2 API, on the other hand, each module can register the event handler it needs without knowing about or interfering with the other module.

addEventListener( ) is paired with a removeEventListener( ) method that expects the same three arguments but removes an event-handler function from an object rather than adding it. It is often useful to temporarily register an event handler and then remove it soon afterward. For example, when you get a mousedown event, you might register temporary capturing event handlers for mousemove and mouseup events so that you can see if the user drags the mouse. You'd then deregister these handlers when the mouseup event arrives. In such a situation, your event-handler removal code might look as follows:

 document.removeEventListener("mousemove", handleMouseMove, true); document.removeEventListener("mouseup", handleMouseUp, true); 

Both the addEventListener( ) and removeEventListener( ) methods are defined by the EventTarget interface. In web browsers that support the DOM Level 2 Events module, Element and Document nodes implement this interface and provide these event-registration methods.[*] Part IV documents these methods under Document and Element and does not have an entry for the EventTarget interface itself.

[*] Technically, the DOM says that all nodes in a document (including Text nodes, for example) implement the EventTarget interface. In practice, however, web browsers support event-handler registration only on Element and Document nodes, and also on the Window object, even though this is outside the scope of the DOM.

17.2.3. addEventListener( ) and the this Keyword

In the original Level 0 event model, when a function is registered as an event handler for a document element, it becomes a method of that document element (as discussed previously in Section 17.1.5.). When the event handler is invoked, it is invoked as a method of the element, and, within the function, the this keyword refers to the element on which the event occurred.

DOM Level 2 is written in a language-independent fashion and specifies that event listeners are objects rather than simple functions. The JavaScript binding of the DOM makes JavaScript functions event handlers instead of requiring the use of a JavaScript object. Unfortunately, the binding does not actually say how the handler function is invoked and does not specify the value of the this keyword.

Despite the lack of standardization, all known implementations invoke handlers registered with addEventListener( ) as if they were methods of the target element. That is, when the handler is invoked, the this keyword refers to the object on which the handler was registered. If you prefer not to rely on this unspecified behavior, you can use the currentTarget property of the Event object that is passed to your handler functions. As you'll see when the Event object is discussed later in this chapter, the currentTarget property refers to the object on which the event handler was registered.

17.2.4. Registering Objects as Event Handlers

addEventListener( ) allows you to register event-handler functions. For object-oriented programming, you may prefer to define event handlers as methods of a custom object and then have them invoked as methods of that object. For Java programmers, the DOM standard allows exactly this: it specifies that event handlers are objects that implement the EventListener interface and a method named handleEvent( ). In Java, when you register an event handler, you pass an object to addEventListener( ), not a function. For simplicity, the JavaScript binding of the DOM API does not require you to implement an EventListener interface and instead allows you to pass function references directly to addEventListener( ).

However, if you are writing an object-oriented JavaScript program and prefer to use objects as event handlers, you might use a function like this to register them:

 function registerObjectEventHandler(element, eventtype, listener, captures) {     element.addEventListener(eventtype,                              function(event) { listener.handleEvent(event); }                              captures); } 

Any object can be registered as an event listener with this function, as long as it defines a method named handleEvent( ). That method is invoked as a method of the listener object, and the this keyword refers to the listener object, not to the document element that generated the event.

Although it is not part of the DOM specification, Firefox (and other browsers built on the Mozilla codebase) allow event listener objects that define a handleEvent( ) method to be passed directly to addEventListener( ) instead of a function. For these browsers, a special registration function like the one just shown is not necessary.

17.2.5. Event Modules and Event Types

As I've noted before, DOM Level 2 is modularized, so an implementation can support parts of it and omit support for other parts. The Events API is one such module. You can test whether a browser supports this module with code like this:

 document.implementation.hasFeature("Events", "2.0") 

The Events module contains only the API for the basic event-handling infrastructure, however. Support for specific types of events is delegated to submodules. Each submodule provides support for a category of related event types and defines an Event type that is passed to event handlers for each of those types. For example, the submodule named MouseEvents provides support for mousedown, mouseup, click, and related event types. It also defines the MouseEvent interface. An object that implements this interface is passed to the handler function for any event type supported by the module.

Table 17-2 lists each event module, the event interface it defines, and the types of events it supports. Note that DOM Level 2 does not standardize any type of keyboard event, so no module of key events is listed here. Current browsers do support key events, however, and you'll learn more about them later in this chapter. Table 17-2, and the rest of this book, omit coverage for the MutationEvents module. Mutation events are triggered when the structure of a document is changed. They are useful for applications such as HTML editors but are not commonly implemented by web browsers or used by web programmers.

Table 17-2. Event modules, interfaces, and types

Module name

Event interface

Event types

HTMLEvents

Event

abort, blur, change, error, focus, load, reset, resize, scroll, select, submit, unload

MouseEvents

MouseEvent

click, mousedown, mousemove, mouseout, mouseover, mouseup

UIEvents

UIEvent

DOMActivate, DOMFocusIn, DOMFocusOut


As you can see from Table 17-2, the HTMLEvents and MouseEvents modules define event types that are familiar from the Level 0 event module. The UIEvents module defines event types that are similar to the focus, blur, and click events supported by HTML form elements but are generalized so that they can be generated by any document element that can receive focus or be activated in some way.

As I noted earlier, when an event occurs, its handler is passed an object that implements the Event interface associated with that type of event. The properties of this object provide details about the event that may be useful to the handler. Table 17-3 lists the standard events again, but this time organizes them by event type rather than by event module. For each event type, this table specifies the kind of event object that is passed to its handler, whether this type of event bubbles up the document hierarchy during event propagation (the "B" column), and whether the event has a default action that can be canceled with the preventDefault( ) method (the "C" column). For events in the HTMLEvents module, the fifth column of the table specifies which HTML elements can generate the event. For all other event types, the fifth column specifies which properties of the event object contain meaningful event details (these properties are documented in the next section). Note that the properties listed in this column do not include the properties that are defined by the basic Event interface, which contain meaningful values for all event types.

Table 17-3. Event types

Event type

Interface

B

C

Supported by/detail properties

abort

Event

yes

no

<img>, <object>

blur

Event

no

no

<a>, <area>, <button>, <input>, <label>, <select>, <textarea>

change

Event

yes

no

<input>, <select>, <textarea>

click

MouseEvent

yes

yes

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey, button, detail

error

Event

yes

no

<body>, <frameset>, <img>, <object>

focus

Event

no

no

<a>, <area>, <button>, <input>, <label>, <select>, <textarea>

load

Event

no

no

<body>, <frameset>, <iframe>, <img>, <object>

mousedown

MouseEvent

yes

yes

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey, button, detail

mousemove

MouseEvent

yes

no

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey

mouseout

MouseEvent

yes

yes

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey, relatedTarget

mouseover

MouseEvent

yes

yes

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey, relatedTarget

mouseup

MouseEvent

yes

yes

screenX, screenY, clientX, clientY, altKey, ctrlKey, shiftKey, metaKey, button, detail

reset

Event

yes

no

<form>

resize

Event

yes

no

<body>, <frameset>, <iframe>

scroll

Event

yes

no

<body>

select

Event

yes

no

<input>, <textarea>

submit

Event

yes

yes

<form>

unload

Event

no

no

<body>, <frameset>

DOMActivate

UIEvent

yes

yes

detail

DOMFocusIn

UIEvent

yes

no

none

DOMFocusOut

UIEvent

yes

no

none


It is useful to compare Table 17-3 with Table 17-1, which lists the Level 0 event handlers defined by HTML 4. The event types supported by the two models are largely the same (excluding the UIEvents module). The DOM Level 2 standard adds support for the abort, error, resize, and scroll event types that were not standardized by HTML 4, but it does not support the key events or dblclick event that are part of the HTML 4 standard. (Instead, as you'll see shortly, the detail property of the object passed to a click event handler specifies the number of consecutive clicks that have occurred.)

17.2.6. Event Interfaces and Event Details

When an event occurs, the DOM Level 2 API provides additional details about the event (such as when and where it occurred) as properties of an object that is passed to the event handler. Each event module has an associated event interface that specifies details appropriate to that type of event. Table 17-2 listed three different event modules and three different event interfaces.

These three interfaces are actually related to one another and form a hierarchy. The Event interface is the root of the hierarchy; all event objects implement this most basic event interface. UIEvent is a subinterface of Event: any event object that implements UIEvent also implements all the methods and properties of Event. The MouseEvent interface is a subinterface of UIEvent. This means, for example, that the event object passed to an event handler for a click event implements all the methods and properties defined by each of the MouseEvent, UIEvent, and Event interfaces.

The following sections introduce each event interface and highlight their most important properties and methods. You will find complete details about each interface in Part IV.

17.2.6.1. Event

The event types defined by the HTMLEvents module use the Event interface. All other event types use subinterfaces of this interface, which means that Event is implemented by all event objects and provides detailed information that applies to all event types. The Event interface defines the following properties (note that these properties, and the properties of all Event subinterfaces, are read-only):


type

The type of event that occurred. The value of this property is the name of the event type and is the same string value that was used when registering the event handler (e.g., "click" or "mouseover").


target

The node on which the event occurred, which may not be the same as currentTarget.


currentTarget

The node at which the event is currently being processed (i.e., the node whose event handler is currently being run). If the event is being processed during the capturing or bubbling phase of propagation, the value of this property is different from the value of the target property. As discussed earlier, you can use this property instead of the this keyword in your event handler functions.


eventPhase

A number that specifies what phase of event propagation is currently in process. The value is one of the constants Event.CAPTURING_PHASE, Event.AT_TARGET, or Event.BUBBLING_PHASE.


timeStamp

A Date object that specifies when the event occurred.


bubbles

A boolean that specifies whether this event (and events of this type) bubbles up the document tree.


cancelable

A boolean that specifies whether the event has a default action associated with it that can be canceled with the preventDefault( ) method.

In addition to these seven properties, the Event interface defines two methods that are also implemented by all event objects: stopPropagation( ) and preventDefault( ). Any event handler can call stopPropagation( ) to prevent the event from being propagated beyond the node at which it is currently being handled. Any event handler can call preventDefault( ) to prevent the browser from performing a default action associated with the event. Calling preventDefault( ) in the DOM Level 2 API is like returning false in the Level 0 event model.

17.2.6.2. UIEvent

The UIEvent interface is a subinterface of Event. It defines the type of event object passed to events of type DOMFocusIn, DOMFocusOut, and DOMActivate. These event types are not commonly used; what is more important about the UIEvent interface is that it is the parent interface of MouseEvent. UIEvent defines two properties in addition to those defined by Event:


view

The Window object (known as a view in DOM terminology) within which the event occurred.


detail

A number that may provide additional information about the event. For click, mousedown, and mouseup events, this field is the click count: 1 for a single-click, 2 for a double-click, and 3 for a triple-click. (Note that each click generates an event, but if multiple clicks are close enough together, the detail value indicates that. That is, a mouse event with a detail of 2 is always preceded by a mouse event with a detail of 1.) For DOMActivate events, this field is 1 for a normal activation or 2 for a hyperactivation, such as a double-click or Shift-Enter combination.

17.2.6.3. MouseEvent

The MouseEvent interface inherits the properties and methods of Event and UIEvent, and defines the following additional properties:


button

A number that specifies which mouse button changed state during a mousedown, mouseup, or click event. A value of 0 indicates the left button, 1 indicates the middle button, and 2 indicates the right button. This property is used only when a button changes state; it is not used to report whether a button is held down during a mousemove event, for example. Note also that Netscape 6 gets this wrong and uses the values 1, 2, and 3, instead of 0, 1, and 2. This problem is fixed in Netscape 6.1.


altKey , ctrlKey, metaKey, shiftKey

These four boolean fields indicate whether the Alt, Ctrl, Meta, or Shift keys were held down when a mouse event occurred. Unlike the button property, these key properties are valid for any type of mouse event.


clientX, clientY

These two properties specify the X and Y coordinates of the mouse pointer, relative to the client area or browser window. Note that these coordinates do not take document scrolling into account: if an event occurs at the very top of the window, clientY is 0, regardless of how far down the document has been scrolled. Unfortunately, DOM Level 2 does not provide a standard way to translate these window coordinates to document coordinates. In browsers other than IE, you can add window.pageXOffset and window.pageYOffset. (See Section 14.3.1. for details.)


screenX, screenY

These two properties specify the X and Y coordinates of the mouse pointer relative to the upper-left corner of the user's monitor. These values are useful if you plan to open a new browser window at or near the location of the mouse event.


relatedTarget

This property refers to a node that is related to the target node of the event. For mouseover events, it is the node that the mouse left when it moved over the target. For mouseout events, it is the node that the mouse entered when leaving the target. It is unused for other event types.

17.2.7. Mixing Event Models

So far, I've discussed the traditional Level 0 event model and the new standard DOM Level 2 model. For backward compatibility, browsers that support the Level 2 model will continue to support the Level 0 event model. This means that you can mix event models within a document.

It is important to understand that web browsers that support the Level 2 event model always pass an event object to event handlerseven handlers registered by setting an HTML attribute or a JavaScript property using the Level 0 model. When an event handler is defined as an HTML attribute, it is implicitly converted to a function that has an argument named event. This means that such an event handler can use the identifier event to refer to the event object. (You'll see later that using the identifier event in an HTML attribute is also compatible with the IE event model.)

The DOM standard recognizes that the Level 0 event model will remain in use and specifies that implementations that support the Level 0 model treat handlers registered with that model as if they were registered using addEventListener( ). That is, if you assign a function f to the onclick property of a document element e (or set the corresponding HTML onclick attribute), it is equivalent to registering that function as follows:

 e.addEventListener("click", f, false); 

When f is invoked, it is passed an event object as its argument, even though it was registered using the Level 0 model.




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net