Simple JavaScript Rollover

The JavaScript rollover is one of the most used, and frankly, misused JavaScript routines on the web. Misused in the sense that in far too many cases it is unnecessary, particularly because one can get the same effect without any JavaScript at all. The menus on this page have the effect—a distinct visual cue when you are over an anchor—using simple CSS. This method is lightweight, valid, has many variations, is semantically correct, and simply works.

There are, however, perfectly good reasons to use a JavaScript rollover. Hey, they are cool. And if one is going to use the JavaScript rollover, one should do it correctly.

This script is based on a script by Christian Heilmann in his “Unobtrusive JavaScript” article at www.onlinetools.org/.

The Process

What the JavaScript rollover does is swap the src attribute of an image for that one another one. The two images must be the same pixel dimensions, and the first, ‘off’ state is coded into the HTML. Then the onmouseover event handler is used to trigger the swap for the ‘on’ image source. Most often, this is further enhanced with an onmouseout event, to swap the image sources back.

To keep everything clear, we are actually swapping the image source, which has the effect of swapping the actual image. In fact, with this script, we are actually doing the swap by replacing part of the file name in the image source attribute.

The Preparation

To begin, we need two images, exactly the same size. With the script we will use, it doesn’t particularly matter if they are both the same format, but usually that is the case. Here’s our images:

off state on state

The one on the left is our ‘off’ state, and the one on the right is the ‘on’. Both are 100 × 100 pixels.

Most important is the file names (not the extension). These are named, respectively, swan_s1.jpg, and swan_s2.jpg. The _s1 and _s2 parts will be what we are replacing. It is also crucial that IDs and classes get put into the HTML correctly. These are the hooks we are using to attach the JavaScript.

In the HTML, we simply put in the off state image (_s1) using the img tag, with the addition of the roll class:

<img src="sc_i/swan_s1.jpg" alt="off state" id="swan" class="roll" width="100" height="100" />

This example is just to get the basic technique down, so we’ll add on to ti later.

The Functions

First, we need to find all the img tags in the document, and test them for the roll class. For the example, and if we were only doing the one image, it would have been as easy to simply use the image ID, but this will be expanded, so we will use a class. The script is very similar to what we used for the new window opener:

function findImg()
	{
	var imgs, i;
	// loop through images
	imgs = document.getElementsByTagName('img');
	for ( i = 0; i < imgs.length; i++ )
		{
		// test for roll class
		if ( /roll/.test(imgs[i].className))
			{
			// add function roll to mouseover
			imgs[i].onmouseover = function(){roll(this);};
			imgs[i].onmouseout = function(){roll(this);};
			}
		}
	}

Next, we do the roll function. This actually does both the onmouseover and onmouseout behaviors. It will test for the off state, and if it exists, will replace it with the on. If the current state is on, it will replace it with the off. The src.replace('_s2','_s1'); is new here, and is a built-in string replacement method. It is actually making use of regular expressions, but in our case we are using the literal string. The method takes two arguments, the first is the string to look for, and the second is the one to replace it with.

function roll(o)
	{
	var src, newsrc;
	// get source of image
	src = o.src;
	// check for off state, replace with on
	if ( /_s1/.test(src))
		{
		newsrc = src.replace('_s1','_s2');
		
		} else {
		// else, back to off
		newsrc = src.replace( '_s2', '_s1');
		}
	o.src = newsrc;
	}

Finally, we add the onload, to run the findImg() function:

window.onload=function()
{
	findImg();
}

Stick it all together, import it with a script tag in the head of the document, and see if it works:

swan 1

And there we have it. Turning the image into an anchor (which is normally the case with a rollover), is a matter of surrounding it with an anchor tag. Same code, same script.

A normal extension

A fairly typical extension of this script would be to have a text link and the image both linked , and both activate the rollover. It’s the thumbnail thing, where you give the viewer a series of thumbnails of images, and corresponding text links.

Preloading

A very common technique with rollovers is to preload the images into the browser cache. To do it, you use a script which loads all the images you need into the Images array for the document. Usually this is done by calling each image individually, something like this:

image1 = new Image();
image1.src = "dir/image1.jpg";

It’s awkward (as in a lot of coding), and it’s equally awkward coming up with a way to loop through a directory or otherwise use a loop. It also takes time. Each image needs to be loaded into the browser cache before the page will display. It comes down to a short lag on slower connections when you activate each particular rollover, or one long lag before the page loads.

First, we need to add the same event handlers to the anchors for the text links in the script, and add the classes to the same anchors in the HTML. Additionally, we will need an ID for each. Our text links will look like this:

<a href="cats.html" class="roll" id="cats">Cozy Kittens</a>

The images will need an addition of an ID, as well as the roll class. This time we need to add the ID attribute. I’ve added “_s” to the ID, to distinguish it from the anchor ID:

<img src="sc_i/cats_s1.jpg" alt="cats" id="cats_s" class="roll" width="100" height="100" />

The first part of the script we need to alter is the findImg() function. Here we add a search for anchors with the roll class, and add the event handlers:

function findImgB()
	{
	var imgs, i, anch, j, title;
	imgs = document.getElementsByTagName('img');
	for ( i = 0; i < imgs.length; i++ )
		{
// test for roll class
		if ( /roll/.test(imgs[i].className ))
			{
// assign behaviors
			imgs[i].onmouseover = function(){roll(this);};;
			imgs[i].onmouseout = function(){roll(this);};;
			}
		}
// get anchors
	anch = document.getElementsByTagName('a');
	for ( j = 0; j < anch.length; j++ )
		{
	// test for roll class
		if ( /roll/.test(anch[j].className ))
			{
	// assign behaviors
			anch[j].onmouseover = function(){roll(this);};;
			anch[j].onmouseout = function(){roll(this);};;
			}
		}
	}

The rollover script needs a bit of tweaking as well. Here, we add a test to see if the image or the anchor is triggering the behavior. If it is the anchor (if ( o.tagName == 'A' )), we grab the image by its ID (k = document.getElementById(o.id+'_s');, using the anchor’s ID and adding (concatenating) the _s. Then we swap as before. If the image is the trigger, we just do the swap:

function roll(o)
	{
	var src, newsrc, k, l;
// is o an anchor?
	if ( o.tagName == 'A' )
		{
		k = document.getElementById(o.id+'_s');
		src = k.src;
		if ( /_s1/.test(src))
			{
			newsrc = src.replace('_s1','_s2');
			} else {
			// else, back to off
			newsrc = src.replace('_s2','_s1');
			}
		k.src = newsrc;
		}
		else
		{
		src = o.src;
		if ( /_s1/.test(src))
			{
			newsrc = src.replace('_s1','_s2');
			} else {
			// else, back to off
			newsrc = src.replace('_s2','_s1');
			}
		o.src = newsrc;
		}
	}

Here it is put together:

This second iteration of the functions replaces the first one, as it can handle both situations. We only need to import the second script.

Other Permutations

With what’s here, there can be many permutations. One of the most sought after for portfolio sites is the slide show. This is where a group of image thumbnails are on a page, and the rollover on a thumbnail causes the full version of the image to appear. Although I'm no fan of these, a way of doing this can be found here. I think the thumbnail thing is OK, but I prefer sending the viewer to a new page for the full image. There’s no huge load lag, or really any need to preload images, and the pages are pretty much templates, so most of it is already in the browser cache. You can see an example of what I mean here.