Working With Events in Netscape 6
November 30, 2000 | Printer-friendly version
This article is a whirlwind primer on the event model of Netscape 6 and Mozilla. I've tried to touch on the main points so that you can spend less time reading boring tutorials like this one and more time doing cool things on the Web. Do not fear events; they are your friends!
You'll need to use Netscape 6 or a recent version of Mozilla to view the examples in this tutorial. And it would also help if you read Scripting For The 6.0 Browsers before reading this, but hey, I'm not your Mom.
A Two-Way Street
Let's take a moment and look at how the current 4.x browsers handle events.
When an event occurs on a web document, such as when one clicks on a link, a special object, called an event object, is created. The event object holds onto data about the specific event that just occurred, such as the mouse coordinates, the type of action (click, mouseover, et al.), what keys were being pressed at the time of the event, and so on. Special functions called event handlers can be written by the developer to intercept and inspect the event object, and respond accordingly.
In Netscape, event objects are usually represented by a lowercase "e", whereas in Internet Explorer, the event object is a property of the window object, named window.event.
When an event occurs, it does not stay in the same spot where it happened. Rather, it then travels through the document, cascading through the elements in the page until it can travel no more. The direction in which it travels -- and the path it takes to get there -- depends on the browser you are using. In Netscape, events will travel inward towards the target of the event. In Internet Explorer, events travel outward towards the browser window.
To clarify this, we need to examine the concepts of event capture and event bubbling.
Event capture was introduced by Netscape. The Netscape browser allows you to "catch" events as they fall inward toward the target element. For example, when you click on a link, the event begins at the document and travels toward the link. By using event capture (via Navigator's captureEvents method), you can intercept the event at the document before it reaches the link. You can then use a special handler method to perform some specific tasks to or with the data from the event object, before passing the event down to the link. The basic flaw with this approach is that the event is never passed back up to the parent elements containing the target.
Event Capture:
document -> link tag -> link text
Internet Explorer introduced the concept of event bubbling. In IE, events move upward from the target element to its parent. IE's event model allows you to intercept events and process them as the event "bubbles to the surface" (the browser window). A shortcoming of this approach is that handlers attached to elements closer to the "surface" may need to process the event data first.
Event Bubbling:
link text -> link tag -> document
The model Netscape 6 (and Mozilla) offers combines some of both approaches with a little added functionality. Let's look.
The path events travel in NS6 is easy to grasp. Events start at the window and travel to the target element (event capture). But upon reaching the target, the event does an about face and travels backward the way it came, until it reaches the browser window again (event bubbling).
In this fashion, each Netscape 6 event is divided into three separate states, or phases. The first phase is when the event is traveling towards its target. This is the event capture phase. Later on, we'll look at how you can intercept events during this phase.
The second phase is when the event is actually at the intended target. Simple enough.
The third phase is the event bubble phase, when the event is traveling back to the browser window. It is during this phase that most handler methods are fired by default.
Event path in Netscape 6/Mozilla:
window -> document -> link tag -> text -> link tag -> document -> window
Great, so now we have the best of both worlds. Netscape 6 allows us to capture events at nearly any level in the document, in any phase. So how do we go about doing that? Read on.
Both Netscape and IE provide a way of attaching event handlers to elements via scripting. The stardard syntax usually looks like this:
element.onEventType = functionName
Here's a more concrete example:
function processLink(){
alert("The link goes to " + this.href)
}
links["myLink"].onclick = processLink
However, there is one basic flaw in this approach: an element can only have one handler of each specified type. If I wanted to add a second, separate onclick handler to the link element in the above example, I'd be out of luck, because there is only one onclick handler per link.
The NS6/Mozilla model gets around this by borrowing a concept from higher programming languages: the event listener. In short, an event listener is a special object that defines an action to be taken when an event occurs. The event listener is then attached to an element, like a link, layer, or what have you. The event listener then, uhm, listens for a specified event to occur. When the event in question reaches the element, the event listener then executes its action, usually a function.
The cool thing about event listeners is that you can have more than one of them listening for the same event on the same object. So if you wanted to have five separate onclick methods attached to the same link, you'd just whip up five seperate event listeners that listen specifically for a click event and assign them to the link.
What's more, you can define event listeners to only respond to events that are in capture phase or bubbling phase. For example, you could have two event listeners that both listen for onmousedown events, with one executing when the event is traveling down to the target and the other only executing when the event is bubbling back up.
Too good to be true? Maybe. In order to take advantage of this great new event listener stuff, we have to learn how to use them correctly.
Listening In
To add an event listener, you use the addEventListener method. Here's the syntax:
object.addEventListener(eventType,functionName,capture)
eventType is a string representing the type of event you want to listen for. Valid examples of this would be "mousedown," "click," or "focus." Note that we do not include the "on" prefix, like an event handler would have. For example: "click" is an acceptable value, while "onclick" is not.
functionName is the function you want executed when the specified event type is caught. This is not a string, but the actual function reference, so don't use quotes! For example, if we want an event listener to execute the function processValue when it detects a mousedown event, we simply pass processValue in the functionName parameter. Passing "processValue" -- as a quoted string -- is incorrect and will prduce an error.
capture is a Boolean (true or false) variable that tells the listener when to execute the function described by functionName. If capture is true, the function is executed when the event is in capture phase (traveling towards it's target). If capture is false, it's executed when the event is in bubbling phase (traveling back up to the browser window). It's important to note that the event listener only "fires" once -- an event listener told to fire during the capture phase will not fire again as the event passes by again during bubbling.
Here is an example in which we attach an event listener to a DIV element, and configure it to execute a function when it hears a mouseup during the capture phase. Here's our DIV tag:
<div id="myDiv" style="position:absolute; left:10px; top:10px; width:100px; height:100px; background-color:red;visibility:visible;">Mouseup me, baby!</div>
Now for the scripting. First, let's write a simple function that will be executed by the event listener:
function heardIt(){
alert("I heard it!");
}
Next, we use the DOM method document.getElementById to get a handle on the DIV element. If you're not familiar with this method or the DOM, check out Scripting For The 6.0 Browsers for details. Since we have to wait and make sure the page is finished loading, we'll encase these next few statements in a function named init and call it with the document's onload handler:
function init(){
divTag = document.getElementById("myDiv");
}
onload = init;
Now let's attach our event listener:
function init(){
divTag = document.getElementById("myDiv");
divTag.addEventListener("mouseup",heardIt,true);
}
onload = init;
That's it! Click here to see the result.
To remove an event listener, you use the removeEventListener method:
object.removeEventListener(eventType,functionName,capture)
The three parameters should be exactly the same ones you passed when you created the listener you now want to remove.
Here's another example that changes the color of a layer on mouseover and mouseout, using the same principles. For variety we'll catch the event on its way back up during the bubbling phase:
function changeMe(e){
var color;
window.status = e.type;
if (e.type=="mouseover") color = "green";
else color = "red";
divTag.style.backgroundColor = color;
}
function init(){
divTag = document.getElementById("myDiv");
divTag.addEventListener("mouseover",changeMe,false);
divTag.addEventListener("mouseout",changeMe,false);
}
onload = init;
Click here to see this example in action.
Whoa! What's that e.type stuff going on in the changeMe function? Ah, how very perceptive of you. What we're doing is using the function to trap the event object -- e -- and examine it at the very moment a mouseover or mouseout occurs on the layer. Remember, the event object contains information about the event as it travels to and fro from the browser window to the target and back again. In this case, we're looking to the event object to tell us what type of event just occurred, and using that to decide which color the layer should be.
As you might have guessed, the type property of the event object is a string containing the name of the event that just occurred. But the Netscape 6/Mozilla event object can tell us so much more. Now is a good time to look at the event object itself and see what other goodies it offers.
The Main Event
Those of you still clinging tenaciously to the old Netscape 4.x model will be glad to know that the event properties pageX and pageX, for retrieving event coordinates relative to the document, and layerX and layerY, for retrieving event coordinates relative to the element postion, are still in effect. pageX and pageY are also reflected in their Mozilla analogues, clientX and clientY, which are also available on the event object.
Other useful properties of the event object include:
type - we've seen this one before. This is a string containing the type of event that ocurred.
target - this is a reference to the event's target object, the object at which the event will turn around and bubble back up the chain.
currentTarget - this is a reference to the object whose event listener invoked the listener's function. This provides information as to where in the document hierarchy the event currently is.
Note that the target and currentTarget objects are not necessarily the same object. For example, let's say you have a green layer within a larger red layer. You decide to attach an event listener to the red layer to listen for mousedown events. Now, to test it, you click (mousedown) on the green layer. At the moment the event listener fires, the event object's target would be the green layer, because that's what you physically clicked on. The currentTarget would be the red layer, because that's the object where the event is at this moment (and subsequently, the one that fired the function).
button - if the event was spawned by a mouse button, this property holds a number indicating which button was pressed or released. This is identical to the Netscape property which, which is still present in Mozilla/NS6.
eventPhase - this is a number indicating the current phase of the event. Its value can be either 0 (zero) for capture, 1 (one) for the moment that the event is actually at the target, or 3 for bubbling.
cancelBubble - this is a Boolean that determines whether or not the event can continue to bubble up to the next element in the hierarchy. The default is usually false, although some events do not bubble by default. If needed, you can use an event listener to set this property to true and halt the event's progress up the chain (unless the event is not cancelable; see below).
bubbles - another Boolean, except this one indicates if the event bubbles by default. In special cases an event may not bubble up the chain. You may need to test this property if your event handling requires bubbling.
cancelable - this Boolean indicates if the event is cancelable or not. (Yes, some events cannot be canceled!)
keyCode - if the event came from pressing a key, this property holds the numeric code for that key.
In addition, there are several properties that track modifier keys. Whereas the old Netscape model contained these in a property called modifiers, the Mozilla/NS6 event object simplifies things by providing the Boolean altKey, ctrlKey and shiftKey properties (although, unlike IE, Mozilla makes no distinction between the left and right sides of the keyboard).
The NS6/Mozilla event model provides some much needed flexibility and functionality. Tinker with it a bit, learn its tricks, and soon you'll be creating responsive, dynamic web documents and applications in no time.
Learn More
- Comment on this tutorial.
- Find out more about Netscape 6.0 at developer.netscape.com
- Learn about DOM, CSS and standards support at www.w3c.org
- Join the cause at the Web Standards Project www.webstandards.org