Understanding The JavaScript Event Model (part 2)

Find out how the Event object can be used to manipulate the dimensions of a Web page and intercept keyboard and mouse events.

Doing More

If you've been following along, you already know the basics of how JavaScript events work - my first treatise on the subject dealt with everything from image swaps to form validation, and also included lots of information on the various events JavaScript exposes to the developer.

This isn't all she sang, though. JavaScript comes with a very capable and powerful Event object, one which opens up a host of new creative possibilities. Use it well, young Jedi - the alternative doesn't bear contemplation.

Back To Basics

First up, a quick recap. The JavaScript event model provides a way for a user to interact with JavaScript. It consists of two basic components, events and event handlers.

An event may be defined, very simply, as an action performed on a Web page - for example, clicking a button, moving the mouse pointer over a hyperlink and so on.

An event handler, as the name suggests, handles an event - it defines the action to be taken by a script when a particular event (or type of event) occurs. Event handlers exist for most of the common events that are generated on a Web page, including mouse movement, mouse clicks, keyboard activity and page loads.

Here's a quick example that might make this theory a little clearer:

<body onLoad="thisFunction()">
...
</body>

If you were to translate the line of code above into English, it would read, "invoke the JavaScript function thisFunction() when the page loads". The event handler in this case is the onLoad handler, which can be used to perform specific actions when a Web page loads into the browser.

JavaScript comes with a whole bunch of event handlers - here's a brief list:

onClick - invoked when the user clicks the specified object

onMouseOver - invoked when the user passes the mouse over the target object

onMouseOut - invoked when the mouse pointer leaves the target object

onSubmit - invoked when the user clicks the Submit button in a form

onChange - invoked when the user changes the contents of a text field

onSelect - invoked when the user selects the contents of a text field

onReset - invoked when the user clicks the Reset button in a form

onLoad - invoked when the target image or document is loaded

onUnload - invoked when the target image or document is unloaded

Whenever an event takes place, JavaScript creates an Event object. This Event object, like all objects, possesses certain properties, which provide additional information about the event generated. So, for example, if you hit a key on your keyboard, JavaScript would create an Event object containing information on the key pressed, the ASCII code, and any additional keys that were pressed in combination with it. Or, if you clicked a mouse button, JavaScript would spawn an Event object containing information on which button was clicked, and where the mouse pointer was at the moment of click.

With all this information at your disposal, it's fairly easy to write client-side scripts that take advantage of it to do new and cool things on your Web page. I'll be showing you a few shortly - but first, take a look at a brief sampling of the properties that a typical Event object exposes:

Property Description (compatibility)
type returns a string value indicating the event type
x returns the horizontal position of the cursor relative to the object
y returns the vertical position of the cursor relative to the object
height returns the height of the object (NN)
width returns the width of the object (NN)
modifiers returns the details of any modifier keys that were held down during a key or mouse event
which returns integer value indicating which mouse button or key was pressed (NN)
keyCode returns Unicode value of the key pressed (IE)
button returns integer value of the mouse button pressed (IE)

Intrigued? Let's do something useful, then.

How's The Weather Up There?

The "height" and "width" properties return the height and width of the window or frame containing the generated event. Here's how it works:

In this example, on clicking the form button an alert box appears, stating the height of the document:

<html>
<head>
</head>

<body>

<form>
<input type="button" value="Click Me" onClick="alert('This document is ' + document.height + ' pixels tall')">
</form>

</body>
</html>

Or you could even do this:

<html>
<head>
<script language="JavaScript">
function changeWidth()
{
    document.width=40
}
</script>

</head>

<body>

<form>
<input type="button" value="Click Me" onClick="changeWidth()">
</form>

This is a really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really long string.

</body>
</html>

Now, when you click the button, you'll see that the document width reduces to 40 pixels, and the incredibly long sentence gets cropped to exactly that width.

Before you get all excited, though, you should know that the "width" and "height" properties only work in Netscape Navigator 4.x.

X Marks The Spot

The "layerX" and "layerY" properties return the horizontal and vertical position, in pixels, of the cursor relative to the layer it is in.

The following example demonstrates:

<html>
<head>
<script language="JavaScript">

document.onmousedown = CoordCheck;

function CoordCheck(e)
{
    if((e.x < 10 || e.y < 10) || e.x > 420 || e.y > 60)
    {
        alert ("OUTSIDE!!");
    }
    else
    {
        alert("INSIDE!!")
    }
}

</script>

</head>

<body>

<layer id="tryme" left="10" top="10" width="410" height="50" bgcolor="cyan">
Try clicking the mouse outside this layer. Then try clicking inside.
</layer>

</body>
</html>

In this case, I have a layer of a specific height and width, and a function that detects when and where the mouse is clicked on the document. If the position of the pointer when the mouse is clicked is within the layer, an alert box shows up with an appropriate message; if the pointer is outside the layer, a different message appears.

The event object generated when the mouse button is clicked is passed to the JavaScript function CoordCheck(), which actually checks the position of the cursor and determines where it is in relation to the layer. I'll be going into the nitty-gritty of how JavaScript's mouse events work a little later; for the moment, concentrate on my use of the "layerX" and "layerY" properties in the example above.

Now, if you tried this example in Internet Explorer, you probably got hit with a couple of error messages. This is because Internet Explorer doesn't support "layerX" or "layerY", preferring instead to use the "x" and "y" properties. Take a look at this rewrite of the previous example, which demonstrates:

<html>
<head>
<script language="JavaScript">

document.onmousedown = CoordCheck;

function CoordCheck()
{
// uncomment the next two lines to see coordinate data
// alert ("The x-coordinate is " + event.x);
// alert ("The y-oordinate is " + event.y);

    if((event.x < 10 || event.y < 10) || event.x > 420 || event.y > 60)
    {
        alert ("OUTSIDE!!");
    }
    else
    {
        alert("INSIDE!!")
    }
}

</script>

</head>

<body>

<div style="background-color:cyan; position:absolute; top:10; left:10; width:410; height:50">
Try clicking the mouse outside this layer. Then try clicking inside.
</div>

</body>
</html>

In addition to these properties, there's also the "screenX" and "screenY" properties, which return the horizontal and vertical position of the cursor relative to the screen. Netscape additionally exposes the "pageX" and "pageY" properties, which returns the horizontal and vertical position of the cursor relative to the page.

Enter The Stalker

Here's another example, this one demonstrating how the current coordinates of the mouse pointer can be exploited to create a simple mouse trail. Take a look at the following example, which has a text string closely following the mouse pointer as it travels across the screen.

<html>
<head>
<script language="JavaScript">

function mouseTracker()
{
    document.all.rock_n_roll.style.visibility="visible";
    document.all.rock_n_roll.style.left = event.x + 20;
    document.all.rock_n_roll.style.top = event.y + 20;
}

document.onmousemove=mouseTracker;

</script>
</head>

<body>

<div id="rock_n_roll" style="font-family:Arial; font-size: xx-small; position:absolute; visibility:hidden; background:black; color: white;">Why are you following me?</div>

</body>
</html>

In this case, every time the mouse is moved, an event is generated and the mouseTracker() function is invoked. This function receives the current X and Y coordinates of the mouse pointer; it then uses these coordinates to move the layer containing the text string to a location 20 pixels behind the pointer.

Here's the Netscape version of the example above - as you can see, it does exactly the same thing, except that it uses the "pageX" and "pageY" properties instead of the "x" and "y" properties.

<html>
<head>

<script language="JavaScript">

function mouseTracker(e)
{
    document.rockroll.visibility="show"
    document.rockroll.pageX = e.pageX + 20
    document.rockroll.pageY = e.pageY + 20
}

document.captureEvents(Event.MOUSEMOVE);
document.onMouseMove=mouseTracker;

</script>

</head>

<body>

<div id="rockroll" style="position:absolute; visibility:hidden; background:black; color:white;"><font face="Arial" size="-2">Why are you following me?</font></div>

</body>
</html>

Of Keys And Clicks

The Event object can also be used to track keyboard events and mouse clicks, and use this information to execute specific actions or commands. Most of this takes place through the Event object's "which" property, which provides information on the key or button pressed. Consider the following example, which demonstrates how this works in Netscape Navigator:

<html>
<head>
<script language="JavaScript">

function whichKey(km)
{
    alert(km.which);
}

document.onkeydown = whichKey;
document.onmousedown = whichKey;

</script>
</head>

<body>

</body>
</html>

In this case, the whichKey() function is called every time a key is pressed or a mouse button is clicked. In the case of a key event, the Event object receives the ASCII value of the key pressed; in the case of a mouse click, it receives a number indicating the mouse button clicked (1 for the left button, 3 for the right button).

Internet Explorer has a slightly different way of doing this - take a look at the following variant, which uses the "keyCode" property to get the key code of the pressed key and the "button" property to get the number of the clicked mouse button.

<html>
<head>
<script language="JavaScript">

function whichKey()
{
    alert(event.keyCode);
}

function whichButton()
{
    alert(event.button);
}

document.onkeydown = whichKey;
document.onmousedown = whichButton;

</script>
</head>

<body>

</body>
</html>

There are a couple of important differences between Internet Explorer and Netscape Communicator when it comes to keyboard and click tracking. The "which" property returns the ASCII value of the key pressed, whereas the "keyCode" property returns the Unicode value. Also, the "keyCode" property is specifically restricted to the keyboard, whereas the "which" property can be used for both keyboard and mouse events.

The "button" property returns an integer corresponding to the number of mouse buttons held down during the event. 0 indicates none, 1 indicates the left button, 2 indicates the right button, and so on.

A Few Modifications

You can also track which of the so-called modifier keys are pressed in combination with a regular key on the keyboard (the modifier keys are the Alt, Ctrl and Shift keys). The "modifiers" property returns an integer or constant indicating which modifier key(s) were held down during the event.

Here's an example:

<html>
<head>
<script language="JavaScript">

function detectKey(mykey)
{

    if (mykey.modifiers == Event.ALT_MASK)
    {
        alert ("You just pressed the Alt key");
    }
    else if (mykey.modifiers == Event.CONTROL_MASK)
    {
        alert ("You just pressed the Ctrl key");
    }
    else if (mykey.modifiers == Event.SHIFT_MASK)
    {
        alert ("You just pressed the Shift key");
    }
    else
    {
        alert ("Nope, that wasn't a modifier key");
    }
}

document.onkeydown = detectKey;

</script>
</head>

<body>

</body>
</html>

In this case, the detectKey() function checks to see which modifier key was pressed, and displays a message appropriately. Note, however, that the script above only works in Netscape Navigator 4.x - later versions of the browser seem to have a problem with it.

Here's another example, this one demonstrating how the same thing works in Internet Explorer.

<html>
<head>
<script language="JavaScript">

function detectKey()
{

    if (event.altKey)
    {
        alert ("You just pressed the Alt key");
    }
    else if (event.ctrlKey)
    {
        alert ("You just pressed the Ctrl key");
    }
    else if (event.shiftKey)
    {
        alert ("You just pressed the Shift key");
    }
    else
    {
        alert ("Nope, that wasn't a modifier key");
    }
}

document.onkeydown = detectKey;

</script>
</head>

<body>

</body>
</html>

Tonight's Menu

Next, let's look at a couple of examples that demonstrate the possible applications of this capability. This next example sets up a small menu containing multiple links, each link associated with a number. The user can select a specific link by pressing the appropriate number key on the keyboard.

Here's the code for Netscape Navigator:

<html>
<head>
<script language="JavaScript">

function menuSelect(e)
{
    // select 1
    if(e.type == 'keydown' && e.which == 49)
    {
        document.location.href="link1.htm"
    }
    // select 2
    else if(e.type == 'keydown' && e.which == 50)
    {
        document.location.href="link2.htm"
    }
    // select 3
    else if(e.type == 'keydown' && e.which == 51)
    {
        document.location.href="link3.htm"
    }
    // select 4
    else if(e.type == 'keydown' && e.which == 52)
    {
        document.location.href="link4.htm"
    }
}

document.onkeydown = menuSelect;
</script>
</head>

<body>

</body>
</html>

And here's the Internet Explorer equivalent:

<html>
<head>
<script language="JavaScript">

function menuSelect()
{
    // select 1
    if(event.type == 'keydown' && event.keyCode == 49)
    {
        document.location.href="link1.htm";
    }
    // select 2
    else if(event.type == 'keydown' && event.keyCode == 50)
    {
        document.location.href="link2.htm";
    }
    // select 3
    else if(event.type == 'keydown' && event.keyCode == 51)
    {
        document.location.href="link3.htm";
    }
    // select 4
    else if(event.type == 'keydown' && event.keyCode == 52)
    {
        document.location.href="link4.htm";
    }
}

document.onkeydown = menuSelect;
</script>
</head>

<body>

</body>
</html>

A quick note on something new here: in both cases, the "type" property has been used to identify the type of event generated. This makes it possible to take action selectively, on the basis of the specific type of event generated.

Reducing The Crime Rate

Ever been to a Web site and tried "borrowing" the JavaScript (by right-clicking and trying to view the source of the page), only to get a little dialog box asking you to cease and desist? Well, it's time for you to get back at all those goody two-shoes, by adding your own little security measures to your site.

<html>
<head>
<script language="JavaScript">

function noRightClick()
{
    if(event.type == 'mousedown' && event.button == 2)
    {
        alert("Sorry, your right mouse button has been disabled.");
    }
}

document.onmousedown = noRightClick;
</script>
</head>

<body>

</body>
</html>

In this case, every time the user clicks the right mouse button, the event will be intercepted by the noRightClick() function, and the normal system behaviour will be replaced with an alert box containing a cease-and-desist message. This usually has more decorative value than anything else - any reasonably adept Web user can still get to the source of your Web page - but it serves to demonstrate the basic concept here.

Endgame

And that's about all we have time for. In this article, I took you a little further into the wild and wacky world of the JavaScript event model, explaining how the Event object can be used to do ever more complex things on your Web page. You learned how to manipulate the width and height of the browser window, how to identify the position of the mouse cursor on a page (and use that location to make something happen on the page), and how to track and intercept keyboard and mouse events.

While this two-part tutorial covers most of the basic techniques associated with event handling in JavaScript, it isn't the end of the line for you. There are some excellent resources out there that you should look at in order to gain insight into more advanced event handling techniques - and I have a list of the better ones:

The JavaScript Programmer's Reference, at http://www.irt.org/xref/

Events and JavaScript, at http://www.webdevelopersjournal.com/articles/jsevents1/jsevents1.html

The Event Object, at http://www.webreference.com/js/column10/eventobject.html

Event Bubbling, at http://www.webreference.com/js/column10/eventbubbling.html

Events in JavaScript: An Inside Look, at http://www.wdvl.com/Authoring/JavaScript/Events/

Have fun, and I'll see you soon!

Note: All examples in this article have been tested on Windows 95 with Internet Explorer 5.x+ and Netscape Communicator 4.x+. Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!

This article was first published on02 Jul 2002.