I prepared a little example of the various event registration models, event properties and event orders. Thus you can get a quick overview of the possibilities and restrictions of event handling.
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. Thererfore 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 Version 4 and higher 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.
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')
Please note that in the registration of an event handler you do not use brackets
()
.
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.
Two notes about
Netscape 4. First of all, if you wish to register an event handler on any
HTML element except for the traditional ones (mouseover on link, keypress on form element),
that element must have position: absolute
in the style sheet.
Furthermore, in theory Netscape 4 requires you to tell it to start looking for the event. This is
done through a special method captureEvents
. The syntax is:
element.captureEvents(Event.CLICK)
For more information see the special Netscape 4 page which will be updated later.
Of course we cannot start using captureEvents
without checking if the browser supports
this rather specialized function. A bit of
object detection is called for. We first see if the method actually exists, and
we use it only if it does.
So the complete Netscape 4 compatible code for assigning the function
doSomething()
as a click
event handler to HTML element element
becomes:
element.onclick = doSomething; if (element.captureEvents) element.captureEvents(Event.CLICK)
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()
als the click
event handler of any HTML element,
that element gets a red background whenever the user clicks on it.
Suppose you want to change the background color of all DIVs onmouseover and restore the
color onmouseout. Using this
correctly, you could do
if (document.getElementsByTagName) var x = document.getElementsByTagName('DIV'); else if (document.all) var x = document.all.tags('DIV'); else return; 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.
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()}
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.
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.
If you wish to go through all event pages in order, you should now continue with the Advanced models page.