GEHC JS Class 2 Image Rollovers --------------- Changing an image on an HTML page with JavaScript. To learn how to do image rollovers, and to understand many of the other topics we will cover, we have to learn about a very important JavaScript topic: Objects In JavaScript, almost everything is an object - certainly, every part of a web page is or can be an object. And if it is an object, then we can manipulate it. Some sample objects: the browser, an image, a form, a text box, a hypertext link The browser itself is the master object, containing almost all other objects. Its name is document. Last week, we used the function document.write and document.writeln. These two functions are called "methods of the document object." If you look in the reference area in the back of the JS text, you will find a wealth of other methods that are a part of the document object (see the Document entry in the Client-side JS Reference). The images in an HTML page make up another part of the document object. They make up an array, or list, of image objects, which are members of the document object. This array is called a property of the document object. No matter how many or how few images are on a page, the document will have a way to access them all. They are referred to by their number, and are numbered in the same order that they appear in the page, from 0 to x, where x is the number of images, minus one. The first image on the page, then, is referred to as document.images[0]. The syntax must be very exact. We can use a pair of HTML properties that are closely linked to JS to do things with our page. o onMouseOver - this property is used when the mouse goes over the tag that the property is used in o onMouseOut - this property is used when the mouse leaves the tag that the property is used in What we want to do is change the image when the mouse goes over the image, and change it back when the mouse leaves the image. ** JS2-1 - our first attempt at an image change - but there is a typo, and that does not allow the change to work This is a good first attempt, but because we refer to "document.image", it fails. There is another problem here that is a common beginner mistake, and that is not knowing what is a JS object and what is a string. In this code: onMouseOver="document.image[0].src=acid_blue.gif; return true;" the JS part is the "document.image[0].src". The string is the name of the file, "acid_blue.gif". To use the string and the image name together, we have to quote the image name properly. ** JS2-2 - this version fixes the images typo, and adds quotes where they should be. Now, instead of an error when we mouse over the image, the image changes. Now, we use "images" instead of "image" and we use single quotes within the JS to group the entire image name into a string. Using the image number, or the array index, is the most portable way to refer to an image in the page, but it is also the easiest to ruin. If your JS code refers to document.images[0], and you put another image before the existing one, in the HTML code, then the new image will automatically be numbered [0] and the old image will become image [1] ** JS2-3 - in this example, we added a new image, but did not change our JS code This is a common problem with JS code - and that is by using numbers, we now cause the first image to change when we mouse over the second image. Note also that mousing over the first image has no effect at all. ** JS2-4 - this code uses object naming In this new version of the code, we remove the dependence upon the image's number, and instead give the image object a unique name. Using the name setting in the img tag, we can provide the page with a brand new object. Even better, we can now refer to the object only by its name, and it doesn't matter where in the document it appears. Note that the image is still a property of the document object. **ASIDE ** JS2-5 - this code replaces "name" with "id", but it won't work as a direct replacement, because "id" does not add an object to document like "name" does ** JS2-5a - this again uses "id", but we use a method of the document object called getElementById. This returns an object that can be used. In our case, there is little reason to go through all this - just use "name" ** The best way to manipulate the images on a web page is to use functions. Functions are bits of code that you write that you intend to be used over and over again. One of the great things about JS is that it allows the page to change after it has been created the first time. The page, then, is not static. This can lead to some interesting effects. ** JS2-6 - this code uses one function to handle mouse over, and another to handle mouse out. Note first that I put the functions in the document's head, and NOT in its body. This is on purpose. It is a bad thing to call a function before it has been defined. If the function is defined in the body, it is possible that the user will trigger the function to be called before it has been read. By placing the functions in the head, we know that before the body is processed, the functions have been seen. What we have in this example are three images - one to start the image, which is used in the img tag itself, a mouse over image, which is used in the doMouseOver function, and a mouse out image, which is used in the doMouseOut function. These functions work because we planned ahead and named the images according to how they would be used: mouse_start.gif mouse_over.gif mouse_out.gif In the doMouseOver function, we accept two parameters, the image object itself, and the base name of the image. In this case, the object's name is document.bottomimg, and the base name of the files is "mouse". ** JS2-6a - same concept as 2-6, but the start and out images are the same image - here we simulate the look of a button press without actually pressing the button ... interesting technique, but may be hard on the user. ** JS2-6b - same as 2-6, but with a key difference Note that with some browsers, particularly MSIE, the "document." part of the reference to a method or property can be left off. But this is not very portable, and though it is more typing, it is a good idea to always use the "document." If you can view this document in Netscape 4.5 or lower, it will not work. It does appear to work fine in Netscape 4.7 and 6.2 and in Firefox. ** Take a moment to discuss browser stats. The difference in code with the functions and with the in-line JS is not very great in the 2-6 example, but take a look at 2-7. ** JS2-7 - a new version of 2-6 with more images In this version, we used essentially the same functions, with some minor changes, and we can use these functions over and over again for each image. Note that each image has a unique name, which is critical. If you look closer, you can see that the doMouseOut and doMouseOver functions are now the same, and we can combine them. ** JS2-8 - combines the doMouseOut and doMouseOver into one function The doImgChange now does the work of two functions - which method you should use depends heavily on what it is you need to do. In the case of 2-8, the only thing that is needed is an image swap, so combining the functions makes sense. But look at 2-9: ** JS2-9 - different tasks are done for the over and the out, so two functions make sense again In this case, we decided not only to change the image, but to change the status line in the browser, too. In this case, it now makes sense to have two versions, one for over and one for out. The over version changes the image, and also sets window.status equal to the image color. Window is another object, which controls some of the elements of the program that is running the document. The status line does not belong to the document, it is part of the window, so we use the window object to change it. The Out function changes the image, and sets the status line back to its default by setting it equal to the empty string. ** JS2-10 - this is a further extension of the 2-9 idea In 2-10, we're simply expanding on the idea in 2-9, and having the doMouseOver function take one extra parameter, the message to display. To come full- circle, see 2-11 ** JS2-11 - this final example combines the functions back into one again In this example, the two functions are once again turned into one, just to show you that there are many different ways in JS to accomplish a task. I suggest picking one that works for you and sticking with it. ** JS2-11a - one more look, using the img tag instead of the a href tag In this last example, we're going to use just the img tag, and include the onMouseOver and onMouseOut properties in the img's HTML code. This method is cleaner, because there is no need for an a href if all you need is a roll-over. MSIE allows almost every single HTML tag to have an onMouseOver and onMouseOut handler, which can make for some very nice looking pages. Unfortunately, Navigator 4.7 will not recognize these events (not even an error), so you're limited if you wish to be compatible, which you may need to be. ** JS2-11b - one final look, using onClick to simulate a link This works, but there are a few issues - no "hand" cursor (which can be fixed with CSS) and the "link" cannot be tabbed into. Preloading ---------- The examples we've seen so far have had a built-in performance problem. The problem is that the browser only downloads the image when it sees it for the first time. When you change the src property of the image object, the .gif file was being seen for the first time, and that triggered a download from the server. The first time (a while back) you probably had a noticeable delay in the display of the over and out images. We can fix that with JavaScript and a method called "preloading." What preloading entails is essentially referencing the images before the user does something that forces the image to display, so that we they do force it to display, it is already cached. The resulting code is more work, but we are trying to write pages that work well for the end user, and not which are the easiest to write. ** JS2-12 - this includes preload code In the head of this page, we have some JS code just as we saw it last week, and a function. The function is a doImgChange function, nothing new. The code outside of the function is our preload code, and illustrates some new concepts. The first are the words "new" and "Image." Images themselves are objects, and we can create an Image object ourselves by using the new keyword. The code var pink = new Image; means to create a new image object, and to create a new variable called pink, and to set pink to the new object. Do the same for the five other colors. In the head of this page, we have created six image objects before a single image has even shown up on the screen. This has no practical effect, though - all we've done is made room for the images. The next step is to reference the images we want to preload. pink.src = "acid_pink.gif"; We are now telling JS to assign the pink Image object to a specific image file found on the server. To do this, the image must be loaded off the server. The same is done with the other new Image objects. We also have a change to the doImgChange function, to use these new images that we have created. The eval routine. image.src = eval(imgname + ".src"); The first part is nothing new - assign image.src equal to some graphic image file. The magic comes in with the eval command. Passed to the eval command are the imgname variable (which is something like "pink") and the string ".src". Combine these two strings together and you get "pink.src". This is exactly the image the pink object points to ... but it is a string, and a string is a completely separate kind of object. eval turns a string into an object, so by doing eval("pink.src"), the result is: image.src = pink.src; and the image changes. If your site is graphics heavy, use preloading for images manipulated by JavaScript and let the HTML page load the rest of your graphics as it can. ** JS2-12a - same thing without eval Finally, we can clean up this code a bit by not using eval(). Eval() is nice, but it has a performance hit. Not a big deal on this page, but it can be on large, complex pages. In this case, we can do away with eval() because the image objects we created are global in scope - we can refer to them in the JS code in the onMouseOver event string, for example. Timed events ------------ All of the events we have looked at so far have been triggered by the user. It is very often helpful to have a timed event, one that happens every ten minutes, or ten seconds, or ten times a second. The setTimeout function is used for just such things. setTimeout(" what to do ", when to do it); The "what to do" is some JavaScript function or code to run, and the "when to do it" is when should the "what to do" be done? The time is measured in milliseconds, so to do something in 2 seconds, enter 2000. For a minute from now, enter 60,000. ** JS2-13 - illustrates a first pass at using a timeout In this code, we preload three images. We also have the doRoll function, which we'll come back to. A thing to notice here is that the img tag has no a href around now, because the event is no longer triggered by the user. What's new here is the use of the onLoad option in the body tag. The onLoad looks at what's inside the quotes, and as soon as the entire page has loaded, runs what is in the quotes. In this case, we run the setTimeout command. The setTimeout command says, in 2 seconds, run the doRoll function. So 2 seconds after the page loads, the fun begins. When doRoll is called, we set the src for the banner object, using the eval technique. Since the first time the page loads, image 1 will display, we will want to show image 2 next. For this reason, we set "var image" to 2. "var imagemax" is set to the highest image number available. In doRoll, after changing the image that is being displayed, we increase the value in "image" by one, so 2 becomes 3. For a safety check, we look to see if the new number is too high. If it is, we reset it back to one. So when image 3 displays, 3 increases to 4, which is too high, and we set it back to 1 for the next time. Lastly, we call setTimeout to call doRoll again in 2 seconds. This process repeats in the background in the browser until the page goes away. ** JS2-14 - enhances this to be even more safe The problem with loading images on a timed basis, is that it is entirely possible that the image we want to show has not been loaded into the cache - and at the end of two seconds, it STILL might not be loaded. by adding the call to banner.complete, we can take care of this issue. The complete property of the image object is set to true only when the image is fully loaded into cache. Until then, it is false. This code now will be sure to display the image for two seconds or more, as long as it takes for the image to load. If the image is not fully ready when the complete property is examined, the image value does not increase. ** JS2-14a - example of an auto-closing window In Groupcast, we often auto-close a window, especially one that only shows to tell a user that something mundane is done - like saving data. This page doesn't close itself, but it does simulate a click of the "back" button. To close the window, we could do "window.close()" instead. Slideshows ---------- A great use of a web page is to show the user a series of graphics and to allow the user to scroll through them at their leisure. This is a slide show, and here is how to implement one in JS. ** JS2-15 - implements a slide show with both next and previous links This page introduces a new concept, that of the user-defined array. The images property of the document object is an array, but it is set up for us by the browser. Creating our own arrays allows us more flexibility. In this case, we write our code to support an array of any length. We have 6 images in this array, but it could easily be 2 or 100. In this case, the array is just a list of the names of graphics. An array is an object itself, and it has properties, like length. The length property tells us how many members of the list exist. So we create an Array object variable named slides, a normal variable that is equal to the slides.length property, and set one normal variable to keep track of what image is currently being shown. The next and prev functions handle all the hard work. The next function just bumps up the current image number, and then looks to see if it is too high. If it is, it starts over at 0. We then use the complete property to hold control of the page until the image is fully loaded. while (document.slide.complete != true); This tells JS to wait until the image is completely loaded. The prev function does the same, but has the opposite issue to deal with - when it wants to display image -1 (of which there is none), it has to reset the image number back to the last image. The final new thing in this page are the a href links, which link not to pages or anchors but to JavaScript code. This tells the browser, when the user clicks on this link, run the piece of JS specified. The void is important, because if it is left off, then when you click on it, you will get a blank page with the word "true" on it, which is the return value of the next and prev functions. JS is a little funky that way. ** If there is time ** ** JS2-15ie - this returns to MSIE's ability to work without the "document." ** JS2-15net - this is a version of 2-15ie that will work in Navigator because we code defensively. This version sets the "ns" variable to 1 - eventually we will figure out how to set ns to 1 if Netscape is being used, or 0 if MSIE is being used.