CIT041J Index > Lecture Notes - Images

CIT041J Lecture Notes

Image Rollovers

EVC logo

In order to implement mouse rollovers such as the one you see at the right, we will need two new event handlers: onmouseover and onmouseout. (You are already familiar with onclick.) The mouseover event occurs whenever the mouse is over an element; the mouseout event occurs when the mouse leaves that element.

In Netscape 4 and Internet Explorer 3, only hypertext links could react to these events. Let’s start with an example that will change the background color when we move the mouse over the links below:

Peach | Light Blue | Neon Pink

Here’s the markup:

<a href="#" onmouseover="document.bgColor='#ffcc99';"
    onmouseout="document.bgColor='white'">Peach</a> |
<a href="#" onmouseover="document.bgColor='#99ccff';"
    onmouseout="document.bgColor='white'">Light Blue</a> |
<a href="#" onmouseover="document.bgColor='#ff99cc';"
    onmouseout="document.bgColor='white'">Neon Pink</a>

The Image object

You learned about image objects in assignment two, where you listed the src property of each of the images in the document.images array. It turns out that you can not only read the src property, you can also set it. When you set the src property of an image, this causes the server to send you the image (if it’s not already cached), and replace the existing image with the new image. It’s as if you changed the src= attribute of the <img> element. Let’s see this in action. The image below is the second image on the page:

black dot

Here is the HTML:

<img src="rollover/blackdot.png" width="100" height="100"
    alt="black dot" />
<form action="#">
    <input type="button" value="Red"
        onclick="change1('rollover/reddot.png')" />
    <input type="button" value="Black"
        onclick="change1('rollover/blackdot.png')" />
</form>

Here is the JavaScript change1( ) function:

function change1( newSource )
{
    document.images[1].src = newSource;
}

red square, blue dot

Now let’s combine the mouseover and the image. When you mouse over the image at the right, the colors and shapes will flip. Here’s the HTML:

<a href="#" onmouseover="change2('rollover/colorflip2.png')"
    onmouseout="change2('rollover/colorflip.png')">
<img src="rollover/colorflip.png" width="100" height="100"
    alt="red square, blue dot" border="0" /></a>

And here’s the JavaScript. Notice that we need a different function, since the image we are manipulating has an index number 2 on this page.

function change2( newSource )
{
    document.images[2].src = newSource;
}

The Numeric Method

Well, you might be thinking, why not make a generic change function like this:

function change( whichImage, newSource )
{
    document.images[whichImage].src = newSource;
}

and use it as on this page (and yes, you should look at it and figure out how it works!)

Even though this works, it’s not a good technique, because it is dependent on the order of the pictures. If you decide to switch the red and yellow pictures, the HTML will have to change. If you were to add a “cyan” picture at the left, you’d have to change the numbers in all the other <a href...> elements.

Giving Images a name

The obvious answer is to give each image a name= attribute, and use the name. After all, that’s what we did to make access to forms easier. If you give a name to each image, you end up with HTML that looks something like this:

<a href="#" onmouseover="change('redImg', 'rollover/red1.jpg')"
    onmouseout="change('redImg', 'rollover/red0.jpg')"><img src="rollover/red0.jpg"
    name="redImg" width="100" height="100" alt="Red" /></a>

<a href="#" onmouseover="change('yellowImg', 'rollover/yellow1.jpg')"
    onmouseout="change('yellowImg', 'rollover/yellow0.jpg')"><img src="rollover/yellow0.jpg"
    name="yellowImg" width="100" height="100" alt="Yellow" /></a>

<-- etc. -->

But this leads to a problem in our function. The following code will not work.

function change( imageName, newSource )
{
   document.imageName.src = newSource;
}

Let’s see what happens when the mouse rolls over the “Red” link. The onmouseover handler tells JavaScript to invoke the change( ) function, and passes redImg as the imageName parameter and rollover/red1.jpg as the newSource parameter. But the assignment statement says to change the source for an image whose name is imageName, not “what’s in the imageName parameter.”

What the code really has to say is document.whatever-is-in-imageName.src = newSource. There are two ways to achieve this effect.

The eval( ) method

This method is the one you’ll see in most rollover scripts. The way it works is to create a string whose content is the command you want to execute. Then you have to tell JavaScript, “OK - take that string and evaluate it as if I had typed it into the script in the first place.” The function will look like this:

function change( imageName, newSource )
{
    var command;
    command = "document." + imageName + ".src = newSource";
    eval(command);
}

Let’s look at the “Red” link again. The parameters get passed as before. The assignment statement will now create a string that works out to document.redImg.src = newSource. That’s the statement that you really want to do. eval(command); tells JavaScript to do that statement. See it in action.

I personally don’t like the eval( ) method. My main objection is that it’s a pain to construct the string correctly. I also note that eval( ) is an “expensive” function—it takes time to parse the string you’ve constructed, and there’s a fair amount of overhead involved in that process. Realistically, this won’t impact the performance of your JavaScript, since the extra time is far overshadowed by the time required to download and/or display the image. It’s just the principle of the thing.

The associative array method

There’s another method to get the image switching, and it depends upon the little-known fact that JavaScript arrays are associative arrays. Not only can you index an array by number, you can also index an array by name.

When the browser sees an <img> element in your HTML document, it not only adds an entry to the images array; it also adds an associative entry to the images array, using the name= attribute as the index. The first image on this page has a name="evclogo" attribute. That means that all of the following will refer to the src of that image:

With this knowledge, we can write the function for the version of the rollover script that uses associative arrays, which you can see in action here.

function change( imageName, newSource )
{
    document.images[imageName].src = newSource;
}

Preloading Images

Consider this: You’re using a slow Internet connection, and you’re on a page with a mouse rollover. You put the mouse on the button...and wait for a few seconds as the other image downloads. We can make this situation better by preloading the highlighted images.

In the <head> of the document, we will create a an array to hold the “highlighted” images and immediately start loading them. To make the code shorter, we will use another array with the file names.


var onImageURL = new Array(       // the "on" images
    "rollover/red1.jpg",
    "rollover/yellow1.jpg",
    "rollover/green1.jpg"
);
var preload = new Array( onImageURL.length );

var imgNum;                     // a loop counter

The body of the following loop does two things:

  1. Creates an Image object in memory. You can think of this as if the browser creates an <img src="" /> element in memory. It’s not in the document, so no new image appears on the screen.
  2. The src of the in-memory image is set to the filename we want to preload. When the source changes, the browser either loads the image from cache, or if it isn’t cached, sends a message to the server asking it to download.
for (imgNum = 0; imgNum < preload.length; imgNum++)
{
    preload[imgNum] = new Image( );
    preload[imgNum].src = onImageURL[imgNum];
}

Note that we use preload.length in the test for the loop. That way, we can preload as many images as we want without having to change the loop code.

All of this code happens in the <head> of the document, outside of a function. It occurs before the text of the page even begins to appear. Once the body of the page starts loading, some (if not all) of the rollover images are on their way from the server. The rest of the page stays exactly as it was. You may see it in action. Since you have probably already loaded these images from the other demos, you won’t see any difference in speed. Trust me, though; it does make a difference.

Another Approach

You may be concerned that there’s a lot of text involved in duplicating the image name and the URLs in the onmouseover and onmouseout attributes. Well, as long as there is an array for the images that you want to preload, and it has the “on” URLs in it, why not add a second array with the URLs of all the “off” images, and a third array with the names of the images? Here’s what it looks like:

var onImageURL = new Array(
    "rollover/red1.jpg",
    "rollover/yellow1.jpg",
    "rollover/green1.jpg"
);

var offImageURL = new Array(
    "rollover/red0.jpg",
    "rollover/yellow0.jpg",
    "rollover/green0.jpg"
);

var imgName = new Array(
    "redImg",
    "yellowImg",
    "greenImg"
);

As long as three arrays stay in step with one another, this approach works wonderfully well. Our preloading code remains the same, but we now have a new function for highlighting images. Our function will take an imageNumber which is an index into our arrays, and a boolean variable named turnOn which tells whether to turn the image on or off:

function highlight( imageNumber, turnOn )
{
    var theURL;
    var theName;

    theName = imgName[ imageNumber ];
    if (turnOn)
    {
        theURL = onImageURL[ imageNumber ];
    }
    else
    {
        theURL = offImageURL[ imageNumber ];
    }
    document.images[theName].src = theURL;
}

Now we change the HTML to look like this. Notice that the JavaScript is much more compact.

<a href="#" onmouseover="highlight(0, true)"
    onmouseout="highlight(0, false)"><img src="rollover/red0.jpg"
    name='redImg' width="100" height="100" alt="Red" /></a>

Hold on a minute!

You are probably thinking, “Why is he going back to using numbers? What happens if we change the order of the pictures? What happens if we need to add a new picture? Won’t we have to recode everything?”

No, you won’t. This time we aren’t using the built-in document.images array, whose numbering changes every time we rearrange or add images. Instead, we’re using the arrays that we constructed ourselves, and over which we have complete control. If you look at the example, you will see that we have added a picture at the left and rearranged two of the other colors. Look carefully at the HTML; the numbers used in the calls to highlight match the order in the arrays in the script. When we add new images, we just add them at the end of all the arrays—it doesn’t matter where they land on the screen.

Variations on a Theme

There are many other ways to do mouse rollovers. They truly are the “cole slaw of the Internet,” with a wide variety of recipes ranging from the really wonderful to the really awful. Here’s one other variation. It presumes that you are basing the URLs for the on and off images on the image’s name attribute:

If you have an image with name="xyz", then the “off” image will be file directory/xyz0.jpg and the “on” image will be file directory/xyz1.jpg

Take a look at the result. The function now gets the image name as its first parameter, not the number in the array. This method requires a little bit of extra text in your HTML, but it relies on names instead of numbers. This makes it easier to read, and that makes people feel better.