reptile7's JavaScript blog
Wednesday, March 12, 2008
Sic 'im, Kitty
Blog Entry #107

Put your mouse cursor in the div below and move it around:

Animated kitten gif

(This demo should work for you if you're using MSIE or a Netscape/Mozilla/Firefox browser but I can't vouch for other browsers.)

Cool, huh? The above effect is coded by a script offered by HTML Goodies' JavaScript Script Tip #91, our focus for this and the next posts. In truth, the original Script Tip #91 Script makes use of a layer and consequently only works with Netscape 4.x; however, we'll see in due course that the script can be made to work with current browsers without too much difficulty.

The Script Tip #91 Script, which is posted here, is mercifully shorter than some of the scripts that we've been analyzing recently:

<body bgcolor="white">

<script language="javascript">
// (C) copyright 1998 greg bland all rights reserved

function trackit(ev) {
document.layers[0].pageX = ev.pageX;
document.layers[0].pageY = ev.pageY; }

document.onmousemove = trackit;

<layer name="mymouse" bgcolor="white" top="100" left="100" z-index="0">
<img src="kittenrunning.gif" />

For the sections that follow, we will assume that the user is browsing with Netscape 4.x.

The animated kitten image and the mymouse layer

The animated kitten image that trails the mouse cursor can be accessed at and is 51px by 48px; I've doubled its dimensions to make it easier to see:

<img id="mycat" src="images/kittenrunning.gif" width="102" height="96" alt="Animated kitten gif" />

The mycat image is wrapped in a layer element container whose start-tag is:

<layer name="mymouse" bgcolor="white" top="100" left="100" z-index="0">

We previously discussed the name, bgcolor, top, and left attributes of the layer element in Blog Entry #94; the z-index attribute, defined by Netscape here, is unnecessary in this case, as Joe himself admits. (At some point in the future, however, we will deconstruct Netscape's "Swimming Fish" DHTML Example, which gets into the z-index concept.)

Mousemove capture

So, how does the script work? The script's effect is based on the 'capturing' of the user's mousemove events.

Let's impose on the browser window a Cartesian-like coordinate system whose origin, (0,0), is at the upper left-hand corner of the document content area. Our horizontal coordinate, x, increases as we move from left to right; our vertical coordinate, y, increases as we move from top to bottom. The upper left-hand corner of the mymouse layer is initially positioned at (100px,100px). Let's say that we move the mouse cursor from (x1,y1) to (x2,y2). The script's script element reads the values of x2 and y2 and respectively assigns them to mymouse's left and top offsets so that mymouse's upper left-hand corner is now at (x2,y2); if we move the mouse cursor to (x3,y3), then the script moves mymouse's upper left-hand corner to (x3,y3), and so on. In dragging the mymouse layer around in this way, the mouse cursor appears to be 'chased' by mymouse.

What allows us to capture the user's mouse cursor movement and read those xn/yn values is the event object, a client-side object that was once part of JavaScript but was spun off to the DOM with the rest of the client-side objects around 1998 or so. You probably know that Level 2 of the DOM has a separate Events Specification (a Level 3 Events Working Draft is in progress); for its part, Mozilla now provides a DOM event Reference section in its Gecko DOM Reference. Before tucking into these materials, however, I recommend that you read the following, much more user-friendly columns at in their entirety:
(1) The Navigator Event Model
(2) The Internet Explorer Event Model
(3) The Cross-Browser Event Model
To a lesser extent, I also recommend Chapter 10 ("Handling Events") in the JavaScript 1.3 Client-Side Guide, which is confusing in places and is not as helpful as the preceding columns.

The Script Tip #91 Script enables the capture of mousemove events at the level of the document object (i.e., anywhere in the document content area) via the command:


• The document object page in the JavaScript 1.3 Client-Side Reference has a short captureEvents( ) method section here, although it's not particularly informative.
• Regarding the captureEvents( ) parameter, I find that the Event part is case-sensitive but the MOUSEMOVE part is not, e.g., mousemove is OK.
• The captureEvents( ) method does not appear in the DOM and is termed "obsolete" by Mozilla. The above command, which is definitely needed by Netscape 4.x to run the script, throws an 'Event' is undefined runtime error when using MSIE; it throws no errors but can be commented out when using Netscape 6+.

We'll use our captured mousemove events to call the script element's trackit( ) function, which moves the mymouse layer around as described earlier and which we'll go through in just a bit. The 'classical' way to tie the execution of the trackit( ) function to a mousemove event in the document content area would be to add an

onMouseMove="trackit( );"

event handler to the body element start-tag. At the time the script was written, however, onMouseMove was not a valid event handler for the document object; indeed, onMouseMove was an event handler for "None [i.e., no objects]" back then. (onMouseMove was unique in this respect; all of the other JavaScript event handlers were associated with at least one object. Currently, [t]he mousemove valid for most elements, according to the W3C.) Fortunately, we can alternatively "register" (associate) onmousemove and the trackit( ) function with the document object via a simple assignment statement:

document.onmousemove = trackit;

This line does not call trackit( ) but assigns its reference to the onmousemove 'property' of the document object. We previously discussed object.onevent = functionName statements in the "Separating the script's HTML and JavaScript" section of Blog Entry #91.

The trackit( ) function

There's no trackit( ) function call in the Script Tip #91 Script; rather, trackit( ) is triggered directly by the user's mousemove events. Here's trackit( ):

function trackit(ev) {
document.layers[0].pageX = ev.pageX;
document.layers[0].pageY = ev.pageY; }

If an event handler function has been registered for an event, then when that event occurs, a corresponding event object is automatically created and passed as a single argument to the handler function. The event object is most often variabilized as e (this is a convention of sorts, much like the use of i as a loop counter variable) but can be given any legitimate variable name, or no name at all: you can use arguments[0] to reference the event object in the handler function if you prefer. As shown above, the Script Tip #91 Script uses the variable ev to represent a user mousemove event.

The event object properties that are relevant to a given event can then be read (and written, in theory at least) via conventional expressions. The following properties apply to a mousemove event: type, target, layerX, layerY, pageX, pageY, screenX, and screenY. The trackit( ) function reads the pageX and pageY properties of each user mousemove event and respectively assigns their values to the pageX and pageY properties of the document's zeroth layer object, i.e., the mymouse layer.
(Although there's no harm in keeping it, the name attribute of the layer element is rendered unnecessary by the use of document.layers[0] expressions here.)

If the mouse cursor is moved from (x1px,y1px) to (x2px,y2px) (cf. the browser window coordinate system described earlier), then ev.pageX will return x2, the cursor's final horizontal position in pixels, and ev.pageY will return y2, the cursor's final vertical position in pixels. That pageX/pageY for a mousemove event return the cursor's final coordinates and not its initial coordinates can be easily verified by monitoring the cursor coordinates with text fields as follows:

(1) Add the form below to the script's document body:

<form action="">
Here's the ev.pageX value: <input name="xcoord" /><br />
Here's the ev.pageY value: <input name="ycoord" />

(2) Add the following statements to the trackit( ) function:

document.forms[0].xcoord.value = ev.pageX;
document.forms[0].ycoord.value = ev.pageY;

In turn, and as you would infer from the preceding "Mousemove capture" section, the pageX and pageY properties of the layer object are respectively analogous to the left and top attributes of the layer element (they're also respectively analogous to the CSS left and top properties in this case, i.e., when the "containing block" is the "initial containing block").

I suppose I should point out that Joe has his definitions of pageX and pageY reversed: it's pageY that is an offset from the top of the browser screen in regards to the layer itself, whereas pageX is the same, but from the left. Joe's [t]hose top and left [layer offset] values are assigned to ev.pageX and ev.pageY statement is also mixed up; in effect, ev.pageX is assigned to the layer's left offset and ev.pageY is assigned to the layer's top offset, as noted above.

A cross-browser Script Tip #91 Script

We'll save this topic for the next entry, in which we'll also discuss the W3C's addEventListener( ) method and roll out a second demo featuring a 'right-looking' kitty.


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)