Traditional event registration model

See section 7C of the book.

On this page I explain the best way to register event handlers to an element, that is: to make sure a certain script runs when a certain event takes place on a certain HTML element.

In the oldest JavaScript browsers event registration was only possible through the inline model. Since DHTML radically changed the way you could manipulate web pages, the event registration model had to be extended and become more flexible. Therefore the browser vendors introduced new event models. Netscape already started in Version 3, Explorer followed in Version 4.

Because Netscape 3 already supported the new registration model, it was a de facto standard before the Browser Wars. Therefore Microsoft was, for the last time, forced to implement this standard in order to keep its browser compatible with uncounted Web pages using Netscape event handling.

So both browsers, and in fact all modern browsers, accept this code

element.onclick = doSomething;

as a correct way to register an event handler. Whenever the user clicks on the HTML element, the function doSomething() is executed. Because it is universally supported, because it is in fact the only way to register an event handling function cross–browser, it’s very important that you thoroughly understand the possibilities and restrictions of this model.

Since no official standard was available when this model was introduced, I call it the traditional event registration model. Meanwhile W3C has standardized event registration, and Microsoft has also created an advanced model (see the Advanced models page), but the traditional model still works fine.

Advanced event registration

From Netscape 3/Explorer 4 onwards, JavaScript recognizes a property for each sort of event that can take place on an element. Therefore most HTML elements have the properties onclick, onmouseover, onkeypress etc. Which HTML elements have which properties — which HTML elements support which events — depends on the browser.

In themselves, these properties were not a radical novelty. They already existed in the oldest JavaScript browsers:

<a href="somewhere.html" onclick="doSomething()">

Here the A tag has a onclick attribute, which in JavaScript becomes a property of the A element. In the oldest browser the event handler is only accessible through HTML attributes in the source code of the page. So if you want this event handler on every link in your page you have to hard–code the function into every single A tag.

With the advent of the traditional event registration model, the onclick, onmouseover and all other event properties of the HTML element are completely accessible through JavaScript. Now you can add, change and remove event handlers without writing the slightest bit of HTML. After you have properly accessed the HTML element through a DOM you can write your function into the property of your choice, like:

element.onclick = doSomething;

Now our example function doSomething() is registered to the onclick property of element and is therefore executed whenever the user clicks on the element. Note that the event name must be all lower case.

To remove the event handler, simply make the onclick method empty:

element.onclick = null;

The event handler is also a normal JavaScript function. It can be executed without an event taking place. If you do

element.onclick()

doSomething() is executed. There’s no real event to go with it, though, so if your function expects one it doesn’t know what to do and produces errors. Therefore this way of executing event handlers is rarely useful.

Microsoft has added the fireEvent() method to Explorer 5.5 and higher on Windows for the same purpose. The syntax is element.fireEvent('onclick')

No parentheses!

Please note that in the registration of an event handler you do not use parentheses (). The onclick method expects to be assigned an entire function. If you’d do

element.onclick = doSomething();

the function would be executed and its result would be registered to onclick. This is not what we want, we want the function to be executed when the event takes place. In addition the function has usually been written to expect an event and if we’d execute it without any such context it would get terribly confused and produce JavaScript errors.

Instead we copy the function doSomething() to the event handler in its entirety. We do not execute it yet, that should only happen when the event actually takes place.

this

In JavaScript the this keyword always refers to the “owner” of a function. In the case of event handlers it is very useful if this refers to the HTML element the event is handled by, so that you have easy access to it.

Unfortunately the this keyword, though very powerful, is hard to use if you don’t know exactly how it works. I discuss its use on another page. Here I give a short summary of its use in the traditional model.

In the traditional model this works as follows; note that it works slightly differently than in the inline model. Now the this keyword is in the function, not in the HTML attribute. The difference will be explained on a separate page.

element.onclick = doSomething;
another_element.onclick = doSomething;

function doSomething() {
	this.style.backgroundColor = '#cc0000';
}

If you register doSomething() as the click event handler of any HTML element, that element gets a red background whenever the user clicks on it.

Anonymous functions

Suppose you want to change the background color of all DIVs onmouseover and restore the color onmouseout. Using this correctly, you could do the following:

var x = document.getElementsByTagName('DIV');
for (var i=0;i<x.length;i++) {
	x[i].onmouseover = over;
	x[i].onmouseout = out;
}

function over() {
	this.style.backgroundColor='#cc0000'
}

function out() {
	this.style.backgroundColor='#ffffff'
}

This code will work, no problem. But since the functions over() and out() are so simple, it is much more elegant to register them as anonymous functions:

...
for (var i=0;i<x.length;i++) {
	x[i].onmouseover = function () {this.style.backgroundColor='#cc0000'}
	x[i].onmouseout = function () {this.style.backgroundColor='#ffffff'}
}

The onmouseover and onmouseout properties expect a function anyway. Instead of copying over() and out(), we immediately define the event handling function in the event handling registration script. Since these function do not have a name, they are anonymous.

The two ways of registering event handlers are completely the same, the only difference is that the second one clutters your code less. I very much like anonymous functions and use them whenever I want to register a simple event handler.

Problems

A distinct drawback of the traditional model is that the onclick property can contain only one function. This becomes a problem when you want to register multiple event handlers for one event.

For instance, suppose you’ve written a module that makes it possible to drag and drop a layer. The module registers an onclick event handler to an element so that clicking on it starts the drag and drop. You have also written a module that silently keep track of user clicks and sends this information to the server onunload, so you can find out how your pages are used. This module, too, registers an onclick event handler to an element.

So what you’d really like to do is

element.onclick = startDragDrop;
element.onclick = spyOnUser;

However, it’s here that things start to go wrong. The second registration of a function to onclick overwrites the first one so that only spyOnUser() is executed when the user clicks on the element.

The solution is of course to register a function that executes both other functions:

element.onclick = function () {startDragDrop(); spyOnUser()}

Flexible registration

But suppose that you don’t use both modules on every page in your site. Now if you’d do

element.onclick = function () {startDragDrop(); spyOnUser()}

you could get error messages because one of the two functions might be undefined. So you have to be more careful in registering your event handlers. When we want to register spyOnUser() while startDragDrop() may (or may not) be registered, we do:

var old = (element.onclick) ? element.onclick : function () {};
element.onclick = function () {old(); spyOnUser()};

First you define a variable old. If the element currently has an onclick event handler, put this event handler in old, if it hasn’t, put an empty function in old. Now you register a new event handler to element div. It is a function that first executes old() and afterwards spyOnUser().
Now the new event handler is added to the element, while previously registered handlers (if any) are preserved.

One last problem: What if you want to remove one of the event handlers, but not the other? At the moment I’m not sure how this should be done. You’d have to edit element.onclick in some way, but I haven’t really studied this problem.

Other models

So we have seen that the traditional event registration model is simple to use, but has some nasty problems as soon as you want to add more than one event handler to the same event on the same element. The W3C event registration model solves this problem quite neatly.

Continue

If you wish to go through all event pages in order, you should now continue with the Advanced models page.