Wednesday, February 06, 2008

Responding to DOM Events using Javascript

Responding to events occurring on a web page using javascript is a necessity these days rather than a preference. Look at the various popular websites today and you will realize that. So, I set out to explore this a little and the following is what I have learned for the first phase, if you want to say so.

A simple example of an event that can occur on a web page is a mousemove event that occurs when the mouse is moved. Events can be organized into three major categories -
  1. UI events - generated by user interaction through an external device (mouse, keyboard, etc.)
  2. UI Logical events - device independent user interface events such as focus change messages or element triggering notifications.
  3. Mutation events - caused by any action which modifies the structure of the document.

Event Propagation

Let us look at the event flow before we jump into the details of event listener registration.

Once an event originates, it is passed through the Document Object Model in three phases -
  1. The capturing phase - the event is first sent to the window, then to the document, followed by each ancestor of the DOM element where the event occurred downwards until it reaches that element.
  2. The target phase - the event is sent to the target DOM element. Target element is the one on which the event has occurred. For example, the target element for a click event is the element which is clicked.
  3. The bubbling phase - the event is sent to each element back upwards until it reaches the window again.
The below image shows how the event is propagated through the three phases. Let us assume that a mouse click event has occurred on the table element. Shaded elements show the order in which the event is propagated. The propagation of the event will start with the capturing phase. Any event listener registered on the window will be called to handle the event followed by document, html, body and so on until the parent of the target element is reached. The target element in this case is the table. This ends the capturing phase after which the target phase starts. In this phase, any click event listener registered on the table element will be called. In the bubbling phase, the event propagates back to the top calling any event listeners registered for the mouse click event.


An event listener being registered on an element may choose to have that listener capture events by specifying the useCapture parameter of the addEventListener method to be true.

Any event handler may choose to prevent further event propagation by calling the stopPropagation method of the event object. If any event listener calls this method, all additional listeners on the current element will be triggered but bubbling will cease at that level. Only one call to stopPropagation is required to prevent further bubbling.

Some events are cancelable. These events have a default action associated with them. An example of this is a hyperlink in a web browser. When the user clicks on the hyperlink the default action is generally to activate that hyperlink. Event listeners have the option of canceling the implementation's default action or allowing the default action to proceed. Cancellation is accomplished by calling the event object's preventDefault method. If one or more event listeners call preventDefault during any phase of event flow the default action will be canceled.


Registering Event Listeners

There are two ways to attach an event listener to an element. First, by using an attribute with script as its value. Second, by calling an element's addEventListener method. The former may only handle bubbling events but tends to be simpler to write. The latter can handle events at any phase and may also be used to attach multiple listeners for an event to an element.

Attribute Event Listeners

To use the attribute form, place an attribute on the element where you want the event listener to be, the name of which should be the event name preceded by the word 'on'. For example, the corresponding attribute for the 'click' event is 'onclick'. The value of the attribute should be some script that should be executed when the event occurs. Typically, this code will be short and just call a function defined in a separate script. An example of responding to a button being pressed:

<input type="button" label="OK" onclick="alert('Button was pressed!');" />

Since the click event will bubble, it is also possible to place the event listener on an enclosing element. In the example below, the listener has been placed on a div and will receive events for both elements.

<div onclick="alert(event.target.tagName);">
<input type="button" label="OK" />
<p>This is a paragraph</p>
</div>

In this example, the click event will bubble up from the button or paragraph to the div, where it is handled. If a second listener (the onclick attribute) were placed on the button, its code will be called first, followed by the handler on the div. Event handlers are passed the event object as an implied argument called 'event'. This is used to get specific information about the event. One commonly used property is the 'target' property of the event, which holds the element where the event actually occured. In the example we display an alert containing the target's tag name. The target is useful when using a bubbling event so that you could have a set of buttons which are all handled by a single script.


DOM Event Listeners

The second way to add an event handler is to call an element's addEventListener method. This allows you to attach an event listener dynamically and listen for events during the capturing phase. The syntax is as follows:

<input type="button" id="okbutton" label="OK"/>
<script>
function buttonPressed(event){
alert('Button was pressed!');
}
var button = document.getElementById("okbutton");
button.addEventListener('click', buttonPressed, true);
</script>


The getElementById() function returns the element with a given id, in this case the button. The addEventListener() function is called to add a new capturing event listener. The first argument is the name of the event to listen to. The second argument is the event listener function which will be called when the event occurs. Finally, the last argument should be true for capturing listeners. You can also listen during the bubbling phase by setting the last argument to false. The event listener function passed as the second argument should take one argument, the event object, as shown in the declaration for the buttonPressed function above.

Note: addEventListener will not work with Internet Explorer, you need to use attachEvent instead.


References:

Registering event handlers section is taken from XUL tutorial mentioned as the second reference above.

No comments: