Team LiB
Previous Section Next Section

Rollover Buttons

One of the most common JavaScript page embellishments is the inclusion of rollover buttons. A rollover button is a button that changes when the user positions the mouse over it or some other event occurs on it. For example, in addition to changing when the user moves their mouse over it, it can change when it is clicked.

To create a basic rollover button, you first will need two, perhaps even three images, to represent each of the button’s states—inactive, active, and unavailable. The first two states are for when the mouse is and is not over the button; the last is an optional state in case you wish to show the button inoperable (e.g., grayed out). A simple pair of images for a rollover button is shown here:

Click To expand

The idea is to include the image in the page as normal with an <<img>> tag referencing the image in its inactive state. When the mouse passes over the image, switch the image’s src to the image representing its active state. When the mouse leaves, switch back to the original image.

Given the following image,

<<img src="imageoff.gif" name="myimage" id="myimage" />>

a reasonable implementation of a rollover might be,

<<img src="imageoff.gif" name="myimage" id="myimage" 
     onmouseover="document.myimage.src='imageon.gif';"     
     onmouseout="document.myimage.src='imageoff.gif';" />>

Of course, you could even shorten the example since you do not need to reference the object path but instead use the keyword this, as shown here:

<<img src="imageoff.gif" 
     onmouseover="this.src='imageon.gif';"     
     onmouseout="this.src='imageoff.gif';" />>

Rollover Limitations

The previous rollover example works in most modern browsers, but under older browsers like Netscape 4, you cannot capture mouseover events on an image in this way, and in very old browsers like Netscape 3, you can’t capture them at all. Furthermore, we may find problems with the script not addressing whether or not the images for the rollover effects have been downloaded by the browser or not. As a gentle introduction to cross-browser problems that emerge as we pursue dynamic effects, we address how to deal with these and other problems.

Event Binding Problems

The first problem we run into with rollovers across browsers and browser versions is that event binding is not supported on the <<img>> tag in many old implementations of JavaScript. So if you want to be backward compatible to Netscape 3 and 4, you can solve the problem by recalling that an image can be surrounded by a link, and links in Netscape 3 and 4 receive onmouseover events. So it is therefore possible to use the link’s event handlers for control purposes. The following short example illustrates this technique, assuming you had two images called “imageon.gif” and “imageoff.gif.”

<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>
<<html xmlns="http://www.w3.org/1999/xhtml">>
<<head>>
<<title>>Quick and Dirty Rollovers<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<<script type="text/javascript">>
<<!--
function mouseOn()
{
  document.image1.src = "imageon.gif";
}
function mouseOff()
{
  document.image1.src = "imageoff.gif";
}
//-->>
<</script>>
<</head>>
<<body>>
<<a href="#" onmouseover="mouseOn()" onmouseout="mouseOff()">><<img
   name="image1" id="image1" src="imageoff.gif" border="0"
   width="90" height="90" alt="rollover" />><</a>>
<</body>>
<</html>>

Lack of Image Object Support

You will find that the previous example doesn’t work in some older JavaScript-enabled browsers, such as Internet Explorer 3 and Netscape 2. In these browsers, images aren’t scriptable, and they therefore don’t support the images[] collection. Thus, regardless of support, we should err on the safe side and try to detect for JavaScript support before trying to modify an image.

The easiest way to make sure the user is running a browser that supports scriptable images is to check for the presence of the document.images[] collection:

if (document.images)
{
  // do image related code.
}

This statement determines whether or not the document.images exists. If the object does not exist, document.images is undefined, so the conditional evaluates to false. On the other hand, if the array exists, it is an object and thus evaluates to true in a conditional statement. We’ll add this check into the next example, which addresses a problem that transcends browser version.

Preloading Images

What will happen if the user starts triggering rollovers when the rollover images haven’t been downloaded? Unfortunately, the answer is a broken image will be shown. To combat this we use JavaScript preloading to force the browser to download an image (or other object) before it is actually needed and put it in cache for later use.

The easiest way to preload an image is, in the <<head>> of the document, to create a new Image object and set its source to the image to preload. This forces the browser to begin fetching the image right away. Unless we have deferred the script execution, the image must be downloaded before the script continues and thus preloading is ensured. To create an Image object, use the object constructor new:

var myImage = new Image();

You can pass in the width and height to the constructor if you wish, but in practice, it doesn’t make much difference if the goal is preloading:

var myImage = new Image(width, height);

Once the object is created, set the src property so that the browser downloads it:

myImage.src = "URL of image";

Consider the following improved example of our image rollovers:

<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>
<<html xmlns="http://www.w3.org/1999/xhtml">>
<<head>>
<<title>>Rollover Example with Preloading<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<<script type="text/javascript">>
<<!--
if (document.images)
{  // Preload images
  var offImage = new Image(); // For the inactive image
  offImage.src = "imageoff.gif";
  var onImage = new Image();  // For the active image
  onImage.src = "imageon.gif";
}

function mouseOn() 
{
  if (document.images)
    document.images.image1.src = onImage.src;
}

function mouseOff() 
{
  if (document.images)
    document.images.image1.src = offImage.src;
}
//-->>
<</script>>
<</head>>
<<body>>
<<a href="http://www.pint.com" onmouseover="mouseOn();"
   onmouseout="mouseOff();">><<img src="imageoff.gif" name="image1" id="image1" 
border="0" width="90" height="90" alt="" />><</a>>
<</body>>
<</html>>

This example is closer to what we need. One remaining problem, however, is that the image names are hardcoded into the script, so it will require significant customization should you wish to reuse it (or even if you wish to add more rollover images to the page). We address that next.

Generalizing Rollover Code

One way to generalize this code to make it more reusable is to develop a consistent naming convention for images, and write JavaScript that assumes this convention. You could, for example, always use the words “on” and “off” as suffixes to each image name indicating the state the image is intended for. You could then automatically compute what image is needed through simple evaluation of the name and the appropriate suffix. This is best illustrated in an example:

<<script type="text/javascript">>
<<!--

function preloadImage(url) 
{
  var i = new Image();
  i.src = url;
  return i;
}



if (document.images)

{  // Preload images
  var homeon = preloadImage("homeon.gif");
  var homeoff = preloadImage("homeoff.gif");
  var productson = preloadImage("productson.gif");
  var productsoff = preloadImage("productsoff.gif");
}

// On input "myimage" this function sets the src of the image with
// this name to the value of myimageon.src

function mouseOn(imgName)
{
  if (document.images)
    document[imgName].src = eval(imgName + "on.src");
}

// On input "myimage" this function sets the src of the image with
// this name to the value of myimageoff.src

function mouseOff(imgName)
{
  if (document.images)
    document[imgName].src = eval(imgName + "off.src");
}
//-->>
<</script>>

Notice how we generalized not only the image swapping function, but also the preloading functionality.

Later on, somewhere in our HTML file we would have appropriately named the images and links with onmouseover and onmouseout handlers to trigger the appropriate parts of the script:

<<a href="home.html" onmouseover="mouseOn('home');"
   onmouseout="mouseOff('home');">><<img src="homeoff.gif" height="50"
   width="100" name="home" id="home" border="0" alt="Home" />><</a>>
<<br />>

<<a href="products.html" onmouseover="mouseOn('products');"
   onmouseout="mouseOff('products');">><<img src="productsoff.gif" height="50"
   width="100" name="products" id="products" border="0" alt="Products" />><</a>>

The complete working example is shown here:

<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>
<<html xmlns="http://www.w3.org/1999/xhtml">>
<<head>>
<<title>>Rollover Example with Preloading<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<<script type="text/javascript">>
<<!--
function preloadImage(url) 
{
  var i = new Image();
  i.src = url;
  return i;
}

if (document.images)
{  // Preload images
  var homeon = preloadImage("homeon.gif");
  var homeoff = preloadImage("homeoff.gif");
  var productson = preloadImage("productson.gif");
  var productsoff = preloadImage("productsoff.gif");}

// On input "myimage" this function sets the src of the image with
// this name to the value of myimageon.src
function mouseOn(imgName)
{
  if (document.images)
    document[imgName].src = eval(imgName + "on.src");
}

// On input "myimage" this function sets the src of the image with
// this name to the value of myimageoff.src
function mouseOff(imgName)
{
  if (document.images)
    document[imgName].src = eval(imgName + "off.src");
}
//-->>
<</script>>
<</head>>
<<body>>

...Page content here...
<<br />>

<<a href="home.html" onmouseover="mouseOn('home');"
   onmouseout="mouseOff('home');">><<img src="homeoff.gif" height="50"
   width="100" name="home" id="home" border="0" alt="Home" />><</a>>

<<br />>

<<a href="products.html" onmouseover="mouseOn('products');"
   onmouseout="mouseOff('products');">><<img src="productsoff.gif" height="50"
   width="100" name="products" id="products" border="0" alt="Products" />><</a>>

<</body>>
<</html>>

Given the script shown, rollovers are limited only by one’s capability to copy-paste and keep names correct. Rollovers have become so commonplace that most WYSIWYG HTML editors can insert rollover code directly. Notice the dialog shown here from Dreamweaver that requests the items that we used in our script.

Click To expand

However, such cut-and-paste or fill-and-go JavaScript is not what we aim to teach. Let’s consider going further than the simple rollover.

Extending Rollovers

Canned rollover codes like the one just presented could be improved. With a little ingenuity you could write a rollover script that you do not need to bind onmouseover and onmouseout code with. Consider making a class name indicating rollovers and having JavaScript loop through the document finding these <<img>> tags and inferring the appropriate images to preload and then dynamically binding the triggering events via JavaScript. This type of very clean rollover could be referenced via an external .js file and cached in all needed pages. This would avoid your need to copy-paste similar rollover code all over your site, which seems to be common practice on the Web and exactly what editors like Dreamweaver create.

Besides improving the coding style of rollovers, we might extend them to perform other functions. For example, a rollover might reveal text or imagery someplace else on the screen as the user moves over a link. A script can be written to reveal a scope note providing information about the destination link. You might even provide an image that users can roll over and learn details about the object by revealing another image. Once you understand the basic idea of rollovers, you’re limited only by your imagination (and your users’ tolerance for fancy effects!).

The following markup and JavaScript illustrate how one such enhancement might work:

<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>
<<html xmlns="http://www.w3.org/1999/xhtml">>
<<head>>
<<title>>Targeted Rollovers<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<<script type="text/javascript">>
<<!--
// Preload all images
if (document.images)
{
  var abouton = new Image();
  abouton.src = "abouton.gif";
  var aboutoff = new Image();
  aboutoff.src = "aboutoff.gif";
  // ... possibly more buttons ...
  var blank = new Image();
  blank.src = "blank.gif";
  var description1 = new Image();
  description1.src = "description.gif";
  // ... possibly more descriptions ...
}
/* Turns the given image on and at the same time shows the description */
function on(imgName, description)
{
  if (document.images)
  {
    imgOnSrc = eval(imgName + "on.src");
    document.images[imgName].src = imgOnSrc;
    document.images["descriptionregion"].src = description.src;
  }
}
/* Turns the given image off and at the same time blanks the description */
function off(imgName)
{
  if (document.images)
   {
     imgOffSrc = eval(imgName + "off.src");
     document.images[imgName].src = imgOffSrc;
     document.images["descriptionregion"].src = "blank.gif";
   }
}
//-->>
<</script>>
<</head>>
<<body>>
<<a href="about.html"
onmouseover="on('about', description1);window.status='Company';return true;"
onmouseout="off('about');window.status='';return true;">><<img src="aboutoff.gif"
  border="0" alt="About" name="about" id="about" width="159" height="57" />><</a>>
<<!-- ... possibly more buttons ... -->>
&nbsp; &nbsp; &nbsp;
<<a href="#">><<img src="blank.gif" name="descriptionregion"
   id="descriptionregion" width="328" height="84" border="0" alt="" />><</a>>
<</body>>
<</html>>

Figure 15-2 shows the rollover code in action.

Click To expand
Figure 15-2: Updating a separate region of the document in response to a rollover

While it would seem from the previous example that JavaScript rollovers are potentially useful, their days are somewhat numbered given that many of these effects are vastly improved with the inclusion of CSS in a Web page.

The End of JavaScript Rollovers?

With the rise of Cascading Style Sheets (CSS), the need for JavaScript-based rollover code has diminished greatly. Already developers have discovered that rollovers are in some sense “expensive” in that they require the download of extra images for the rollover effect. For simple navigation items, this penalty is just not worth it and many Web developers are opting instead for simple rollover effects using a CSS :hover property, like so:

<<style type="text/css">>
a:hover    {background-color: yellow; font-weight: bold;}
<</style>>

If you take the idea of hover further you might even change the background image of a region to create a more graphical rollover. To do this, set the rollover region to contain a transparent GIF with some alt text and then swap the background-image on hover. With this simple CSS you now have a degradable and accessible graphical rollover effect without any JavaScript! The following example illustrates this idea.

<<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>
<<html xmlns="http://www.w3.org/1999/xhtml">>
<<head>>
<<title>>CSS Rollover Example<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<<style type="text/css">>
a img {height: 35px; width: 70px; border-width: 0; background: top left 
no-repeat;}

a#button1 img {background-image: url(button1off.gif);}
a#button2 img {background-image: url(button2off.gif);}

a#button1:hover img {background-image: url(button1on.gif);}
a#button2:hover img {background-image: url(button2on.gif);}
<</style>>
<</head>>
<<body>>
<<div id="navbar">>
 <<a id="button1" href="http://www.javascriptref.com/">><<img src="blank.gif" 
alt="JavaScript Ref">><</a>>

 <<a id="button2" href="http://www.google.com/">><<img src="blank.gif" 
alt="Google">><</a>>
<</div>>
<</body>>
<</html>>

With CSS, you can go even further and address the multiple image download problem that plagues rollovers. For example, we might create one large image of navigation buttons in a menu in their on state and one large image of the buttons in their off state, as shown here:

Then we would use CSS clipping regions in conjunction with either :hover rules or JavaScript to reveal and hide pieces of the image to create the rollover effect. With a simple approach like this we would cut down eight image requests if the buttons were separated to two since they are together. While CSS is quite powerful by itself and it can be used to replace some simple visual effects like rollovers, we’ll see that it is even more powerful when combined with JavaScript to create DHTML effects.


Team LiB
Previous Section Next Section