Events and Rollovers

(Reading: We strongly suggest you read pages 148 and 162-163 of JavaScript: A Beginner's Guide with these notes.)

Rollovers Overview

We've all seen the rollover mania that grips cyberspace. It's manifested in simple, “click here” rollovers such as the ones on this homepage. Notice that as you roll over a black key on the keyboard "menu" on the left, the key is highlighted. There are also more elaborate double rollovers such as the ones at the Quantitative Reasoning Program. Not only are the links changing color, but some explanatory text appears on the right. (The rollovers on the Wellesley College home page are different and much more complex. We will not discuss them in this course.)

Elementary rollover effects can be done using the CSS :hover pseudoclass. For simple cases like text, background colors/images, etc., that is the better way to go. But doing rollovers with JavaScript is more powerful, and more fun!

JavaScript rollovers may seem complicated but the basic idea is actually rather simple. Typically, you have two images, such as the following (for a website booking tickets to the north pole):

You display the default image as usual and the browser watches the mouse's position. You ask the browser to execute some JavaScript when the mouse rolls over the image. When that happens, your JavaScript code swaps images (in this case from the image with the green background to the one with the red background).

To understand this in more detail, we have to distinguish among:

In a sense, the page element created by the <img> tag functions like a picture frame. A rollover replaces the contents of the picture frame with another picture.

Events

An event is some action that takes place. A browser can detect some events that might be of interest to the author of a web page. For example, the browser can determine when a user:

There are common JavaScript events defined on most HTML page elements. You can write JavaScript code that runs if and when one of these events takes place. JavaScript code whose execution is triggered by an event is called an event handler.

HTML tags use certain attributes to allow you to associate your JavaScript event handler with an event associated with a particular page element:

As you can see, the name of the attribute corresponds to the event that is being handled. (All attributes that specify event handlers begin with on.) The event handler is the value of the attribute. When that event occurs, the JavaScript code in the event handler is executed. Here's an example that just does a simple alert:

take the polar express

<img src     = "polar-green.png"
     alt     = "take the polar express"
     onClick = "alert('thanks for clicking!');">

Here are some more examples of events.

Implementing Simple Rollovers

In order to implement an image rollover, the event handler needs to refer to the page element that will have its image data modified. Usually, of course, that image is the same one that has the event-handler attribute, but not always (as we will see). In that special case, we can use a special JavaScript variable named this, which refers to the HTML object associated with the event.

take the polar express

<img src         = "polar-green.png"
     alt         = "house icon"
     onMouseOver = "this.src = 'polar-red.png';"
     onMouseOut  = "this.src = 'polar-green.png';">

Let's talk about the code in the onMouseOver event handler. It's traditional to use the up-and-down capitalization for that and similar event handlers, since it's easier to see the words it comprises. However, as you know, HTML is not case-sensitive, so you can specify the name of the attribute in all-lower or all-upper case.

The value of the attribute is all the stuff enclosed by double quotes, and the value of the attribute is automatically treated as JavaScript code, without having to specify <script> tags.

In this case, the JavaScript code is an assignment statement that changes the value of this.src. The this variable may seem magical: its value automatically changes from place to place and it always means “the current object.” In this instance, it means the image object created by the <img> tag that contains the event handler code; it's as if the src attribute of the tag is changing. As you know, the src attribute determines which image file is displayed, so changing the src attribute causes a new image to be downloaded and shown.

The other event handler is almost identical. It re-loads the original image data when the mouse is no longer over the image.

In JavaScript, as in English, the word “this” depends on context. In the example above, this means the JavaScript image object created by the <img> tag. To do more complex rollovers, we'll have to be able to refer to them. However, we can't just say “that” because the browser should respond “which?”

Implementing More Complex Rollovers

To refer to any image other than this one, we have to give it a name or an identifier. To do that, we can use the id attribute. (Some old code still uses the name attribute, but name has been deprecated and id is preferred. You can use id to label pretty much any HTML tag.)

Let's start with using id for just a simple rollover. The following code is equivalent to the rollover in the previous section:

take the polar express

<img src         = "polar-green.png"
     id          = "pic1"
     alt         = "house icon"
     onMouseOver = "document.getElementById('pic1').src
                        = 'polar-red.png';"
     onMouseOut  = "document.getElementById('pic1').src
                        = 'polar-green.png';">

In this example, the id is pic1, which is fine for this example, but in general it's better to label the image in a way that is meaningful to you as the web author, so that you can remember what role the image plays. For example, label it infobox or caption or whatever.

Now, if we want some other image to change when we roll over this one, all we need to do is use id to label the one we want and use the label in our JavaScript code. Here's an example:

take the polar express take the polar express

<img src         = "polar-green.png"
     id          = "picleft"
     alt         = "take the polar express"
     onMouseOver = "document.getElementById('picright').src 
                          = 'polar-red.png';"
     onMouseOut  = "document.getElementById('picright').src 
                          = 'polar-green.png';">
<img src         = "polar-green.png"
     id          = "picright"
     alt         = "take the polar express"
     onMouseOver = "document.getElementById('picleft').src 
                          = 'polar-red.png';"
     onMouseOut  = "document.getElementById('picleft').src 
                          = 'polar-green.png';">

To do a double rollover, you just have to change the src of more than one img. For example:

take the polar express take the polar express

<img src         = "polar-green.png"
     id          = "left2"
     alt         = "take the polar express"
     onMouseOver = "document.getElementById('left2').src 
                          = 'polar-red.png';
                    document.getElementById('right2').src 
                          = 'polar-red.png';"
     onMouseOut  = "document.getElementById('left2').src 
                          = 'polar-green.png';
                    document.getElementById('right2').src 
                          = 'polar-green.png';">
<img src         = "polar-green.png"
     id          = "right2"
     alt         = "take the polar express"
     onMouseOver = "document.getElementById('left2').src 
                          = 'polar-red.png';
                    document.getElementById('right2').src 
                          = 'polar-red.png';"
     onMouseOut  = "document.getElementById('left2').src 
                          = 'polar-green.png';
                    document.getElementById('right2').src 
                          = 'polar-green.png';">

Notice that the code in the event handler can get rather long. There are many ways to abbreviate the code, which we will learn when we get to functions, but even without that, length is not a problem. The browser doesn't care how long a line is, so you could just let it stretch, even to a hundred characters or so. Long lines, however, are difficult for people to read, so putting in line breaks is a good idea. The browser doesn't object to line breaks within an event handler (as illustrated above). Typically, you would break the line at a logical location within the JavaScript code, such as after the semi-colon. The only restriction is you must not break the line within a JavaScript string literal, such as within either of the filenames (e.g., at the hyphen or the .png above).

Here are some more examples of rollovers using other kinds of events.

You may sometimes see JavaScript code in which

document.getElementById('identifier').src

is written more succinctly as

identifier.src

Unfortunately, while this works in many browsers, it does not work in all browsers. For this reason, we recommend that you do not use this abbreviation. For example, in Spring, 2008, the following worked in Firefox and IE, but not in Safari:

take the polar express take the polar express

<img src         = "polar-green.png"
     id          = "left3"
     alt         = "take the polar express"
     onMouseOver = "left3.src = 'polar-red.png';
                    right3.src = 'polar-red.png';"
     onMouseOut  = "left3.src = 'polar-green.png';
                    right3.src = 'polar-green.png';">
<img src         = "polar-green.png"
     id          = "right3"
     alt         = "take the polar express"
     onMouseOver = "left3.src = 'polar-red.png';
                    right3.src = 'polar-red.png';"
     onMouseOut  = "left3.src = 'polar-green.png';
                    right3.src = 'polar-green.png';">
Exercise 1

This link has a nice example of image swap behavior. Try to figure out how it was done, then check the code.

The rollovers above swap images very nicely, but they're not active links, which is the most common use of a rollover. A rollover link is nice because the user gets visual feedback that the mouse is positioned correctly before they click.

To make an image into a hyperlink, all you need to do is wrap it with the anchor (<a>) tag:

take the polar express

<a href = "http://www.imdb.com/title/tt0338348/"><img
     src         = "polar-green.png"
     alt         = "take the polar express"
     onMouseOver = "this.src='polar-red.png';"
     onMouseOut  = "this.src='polar-green.png';"></a>

Notice that the code is not indented as nicely as we've argued that it should be: in particular, the <img> tag is at the end of a line, instead of being neatly indented under the <a> tag. In this case, aesthetics has to give way to function, because if we indent the <img> tag below the <a> tag, the line break will end up causing blue-underlined whitespace in the browser, which will look ugly. Whenever you are using the <a> tag, remember that everything between the start tag and the end tag will end up with blue underlining (or whatever your stylesheet specifies), and so you have to be careful, even with “invisible” stuff like spaces and line breaks. It is always safe, however, to break lines between attributes of a tag, as we did between <img> tag and its src attribute.

Exercise 2

This link extends the previous exercise to do hyperlinks. Try to figure out how it was done, then check the code.

Why use the <a> tag at all? Why not have a JavaScript event handler that responds to a mouse click by changing the browser's location to the desired page? The code would look like the following:

take the polar express

<img src         = "polar-green.png"
     alt         = "take the polar express"
     onClick     = "window.location 
                         = 'http://www.imdb.com/title/tt0338348/';"
     onMouseOver = "this.src = 'polar-red.png';"
     onMouseOut  = "this.src = 'polar-green.png';">

There is a certain aesthetic beauty to code like this, at least to computer scientists, because of its simplicity. However, we should eschew this simplicity and prefer the more complex code that uses the <a> tag. The reason is that some older browsers don't support event handlers or JavaScript, and some users disable JavaScript in their preferences, or are using screen-reading software that doesn't handle these events in the same way. In short, if the JavaScript doesn't work, for any reason, you still want your web page to work. This is what is known as graceful degradation.

One aesthetic disadvantage of the image link that we recommend (the one using the <a> tag) is the blue border that some browsers will display. The blue used in links is a strong hint to the user that something is a link, so the blue border is generally a good thing unless you're quite certain that your user understands that this image is a link. But, in many cases, you are certain that is so (for example, navigation bars make the intention clear), and you want to get rid of the blue border.

To eliminate the border, you use CSS:

take the polar express

<a href = "http://www.imdb.com/title/tt0338348/"><img
     style       = "border-style: none"
     src         = "polar-green.png"
     alt         = "take the polar express"
     onMouseOver = "this.src = 'polar-red.png';"
     onMouseOut  = "this.src = 'polar-green.png';"></a>

Of course, you should do this with some external style sheet. You could, for example, use the following:

A > IMG {  border: none; }

The A > IMG matches an image tag immediately inside an anchor tag. See the W3schools CSS reference.

Exercise 3

This link extends the previous exercise to do borderless rollovers. Try to figure out how it was done, then check the code.

Implementing Simple Rollovers with Changing Text

Now that we've mastered rollovers, let's look at one interesting variation, namely changing some text on the page along with the image. Here it is in action:

take the polar express

<a href = "http://www.imdb.com/title/tt0338348/"><img
     src         = "polar-green.png"
     alt         = "take the polar express"
     onMouseOver = "this.src = 'polar-red.png';
                    var desc = document.getElementById('description');
                    desc.innerHTML = 'See the IMDB page about the movie The Polar Express';"
     onMouseOut  = "this.src = 'polar-green.png';
                    var desc = document.getElementById('description');
                    desc.innerHTML = '';"></a>
<span style="margin-left: 30px;" id="description"></span>

We can also put dynamic text into the document. In this case, we want to get the HREF of the tag that this image is inside, so we can go up to a parent node.

take the polar express

<a href = "http://www.imdb.com/title/tt0338348/"><img
     src         = "polar-green.png"
     alt         = "take the polar express"
     onMouseOver = "this.src = 'polar-red.png';
                    var desc = document.getElementById('description2');
                    desc.innerHTML = 'See the IMDB page '+
                                     this.parentNode.href+
                                     ' about the movie The Polar Express';"
     onMouseOut  = "this.src = 'polar-green.png';
                    var desc = document.getElementById('description2');
                    desc.innerHTML = '';"></a>
<span style="margin-left: 30px;" id="description2"></span>

Preloading

One problem that can arise with rollovers is that when the mouse rolls over the image for the first time, the second image has to be downloaded across the network, and that can cause a noticeable delay. When that happens, the users don't get the visual feedback they expect, and confusion can ensue, or at least annoyance.

Browsers automatically cache anything they download. “To cache” (pronounced “cash”) means to keep a copy in memory or on the local disk. The browser cache makes references to previously loaded images pretty fast. Thus, the solution is to make sure that the second image is downloaded before it is needed. This is called preloading.

We do this by downloading rollover images when the page loads, but we keep them in “offstage” (undisplayed) objects, rather than in <img> tags — they won't be visible until they are needed.

The preloading code would typically be put in the head of the document, just to get started on loading the images asap. If we wanted to preload the red Polar Express image, we could do the following:

<script type="text/JavaScript">
    var polar_red = new Image();
    polar_red.src = "polar-red.png";
</script>

Here's what's going on. The first line of code creates an object to hold image data. This is an Image object, not surprisingly. The object is placed in a variable called polar_red. It's not important what we name the variable; we could have called it picture or just p. The second line of code assigns a URL to the src attribute of the object, thereby loading the image data across the network and into the object. At this point, the image data is cached and any uses of the polar-red.png file will load very quickly.

If we had several images to preload, we would use a different variable for each one. For example:

<script type="text/JavaScript">
    var nav1 = new Image();
    nav1.src = "home-button-over.png";
    var nav2 = new Image();
    nav2.src = "history-over.png";
    var nav3 = new Image();
    nav3.src = "eboard-over.png";
    ...
</script>

The technique we've just described has a flaw: the images that we will need later start loading before the ones that are actually in the page and that we want to see. In many cases, the images will download relatively quickly and the user won't even be aware. But if the image files are big (which is what makes preloading worthwhile in the first place), we don't want to load the offstage images before the onstage ones.

What we'd really like to do is to start preloading the offstage images as soon as the document has finished loading. (We could put the <script> element at the end of the page, instead of in the head, but we're too likely to overlook code that's down there.)

It turns out that there is an event defined by the page finishing loading. It's the load event. You can use it thus:

<body onload="var img1 = new Image(); img1.src='big_image_file.jpg'">

There are even more sophisticated ways of using the onload event handler, which we'll learn when we learn more about functions.

Slide Shows

We can extend the rollover idea to a slide show. Each time you click on the following image, the image changes. It cycles through six possibilities.

one face of a six-sided die

Almost all the work is done in the event handler, which increments the slide number, using the remainder operator (the percent sign) to start over again when we reach 6. The code is as follows:

<img id      = "the_slide"
     src     = "../../Images/die1.gif"
     height  = "70"
     width   = "70"
     onClick = "slideNumber = (slideNumber % 6) + 1;
                document.getElementById('the_slide').src 
                  = '../../Images/die' + slideNumber + '.gif';">

The only piece that is missing is some code to initialize the value of slideNumber. That is done by the following simple script:

<script type = "text/JavaScript">
  var slideNumber = 1;
</script>

The slide-show code is organized into two pieces: there's the initialization script that goes in the body of the page, and the event handler that advances the slide. Note that we don't put the line of code that initializes the slideNumber variable in the event handler. Why not? Because if we did so, the slide number would start over at one every time we clicked on the image, and so we would only see the first slide.

Note that it's not important that you click on the image to advance the slide show. You could also have a button to do that.

Numbering and Naming Image Files

One trick that helped the dice slide show was that the names of the image files had a number in them, so we could mathematically generate a number and thereby generate a filename. What can you do if your image files don't have that convenient pattern?

Caveat

We've used the alert() function in some of our examples because it's obvious when it runs. You can use alert() anywhere in your JavaScript code. However, you can't use document.write() in an event handler. Why? Not because of any rule in JavaScript, but because it doesn't make sense.

You use document.write() within the <script> tag as part of creating the body of a web page, but an event handler executes after the web page has been written. There's no more content left to write, so the document.write() either has no effect or causes the browser to hang, thinking that you're writing a new web page. So, remember,

Only use document.write() within the <script> tag, not as part of an event handler.

Rollovers Aren't Just for Images

Events can be attached to most HTML page elements (at least in modern browsers), and rollovers can be used to change all sorts of things. In particular, JavaScript can access and alter all of the CSS style properties of a page element (color, font, width, etc.). Click here to see an example with rollover and click events attached to table cells and a table.

Further Information and Examples

Note that beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.

Arrays

An array can be thought of as a variable that has numbered slots, like parking spaces in a pay parking lot. The number of a slot is called its index. The numbered elements are accessed by putting the index (or a variable containing the index) in square brackets after the variable name. Here's an example of using an array to map numbers to filenames for a slide show:

First, we have to initialize the array, storing the elements in their numbered locations:

<script type="text/JavaScript">
  var filenames = new Array();
  filenames[0] = 'lions.jpeg';
  filenames[1] = 'tigers.jpeg';
  filenames[2] = 'bears.jpeg';
</script>

Your mental picture of the preceding code is of a big box labeled "filenames"; this is the variable. Inside that big box is a set of three smaller boxes all in sequence, like an egg carton; this is the array. The three smaller boxes are labeled 0, 1 and 2; these are the slots of the array. Inside the three smaller boxes are the strings 'lions.jpeg', 'tigers.jpeg', and 'bears.jpeg'.

Thus, we've stored three strings into the one variable called filenames, but in numbered slots within the variable.

Later, if we want a particular filename, we use the appropriate number. For example, the following code would cause 'tigers.jpeg' to be displayed in the alert box:

<script type="text/JavaScript">
  alert(filenames[1]);
</script>

However, we don't want to use a fixed index, we want an index that will change depending on where we are in the slide show. In this example, the index will take on the values from 0 to 2. Imagine there's a variable called slideNumber. Then, the following code will give us one of the elements of the array, as long as slideNumber is 0, 1 or 2:

filenames[slideNumber]

So, to do the slide show, we start slideNumber off at 0, increase it by 1 whenever the user advances the slide, and when slideNumber exceeds 2, we start it over again at 0.

It turns out that an easy way to start over again at 0 is to use arithmetic mod 3; that is, 0-2 are the remainders when you divide by 3, so we can use a computation based on "% 3":

<script type="text/JavaScript">
  slideNumber = (slideNumber + 1 ) % 3;
  the_slide.src = filenames[slideNumber];
</script>

These two lines of code are deceptively concise. The first line increments the slide number, but uses the "% 3" to make sure that the result is always 0, 1 or 2. The second line uses that number as an index into the array called filenames. The filename that was stored in that numbered slot is then made the SRC of the image.

Summary