Web Developer 2

Timing is Everything

JavaScript includes a method with the window object called setTimeout() that allows you to apply a fourth dimension to your pages.  We've already seen the first three dimensions using CSS - the top, left, and z-index properties.  Using setTimeout(), we gain the ability to trigger functions at set intervals thereby creating dynamic web pages.  The setTimeout() method is used heavily in Dynamic HTML - which we'll cover in greater detail a little later on.  For now, we'll use the setTimeout() method in conjunction with our image swapping capabilities and knowledge of arrays to create a slide show.

The Slide Show

Slide shows are another great way to show your users a lot of information in a small piece of screen real estate.  Let's take a step back and see if we can state our project in general terms:  we'd like to display a series of pictures, in order, so that when we reach the last picture, we loop back to the first picture and start over again.  I hope you can see that the "in order" part of our problem can be solved nicely with an array and the "series of pictures" part can be solved by image swapping.  So the only new part about this project will be the looping of the pictures and the timing between the slides.

For this project (and all those that follow) we'll be using relative paths.  It is customary to separate your files by media type when you are doing web development so that you end up with folders within your site that contain only certain types of media.  You might have separate folders for images, sounds, scripts, applets, styles, and anything else your site requires.  In the root folder of the site, you'll store the HTML pages and reference all of your media from there.  For this project, we'll create a folder called "images" to hold our image files just below the folder that contains our HTML page.  Here are the image files.  Click the link, then right click on the image and choose Save Picture As...  Store the images in a folder called images inside the folder where you'll store the HTML document.

madison.jpg

carson.jpg

hayden.jpg

kids.jpg

Now let's get busy creating the HTML page.  We'll start by creating a simple HTML page that contains only the first picture in the slide show:

<html>
<head>
<title>slideshow</title>
</head>

<body>
<p><img name="slide" border="0" src="images/madison.jpg" width="230" height="226"></p>
</body>
</html>

Notice the use of the relative path in the src attribute of the <img> element.  We are referencing the madison.jpg file in a folder called images which is inside the folder containing our HTML file.  The next step is to create the array inside a <script> element.  The script will reside inside the <head> element:

<script language="JavaScript">
slideArray = new Array();
slideArray[0] = new Image();
slideArray[0].src = 'images/madison.jpg';
slideArray[0].text = 'Madison';
slideArray[1] = new Image();
slideArray[1].src = 'images/carson.jpg';
slideArray[1].text = 'Carson';
slideArray[2] = new Image();
slideArray[2].src = 'images/hayden.jpg';
slideArray[2].text = 'Hayden';
slideArray[3] = new Image();
slideArray[3].src = 'images/kids.jpg';
slideArray[3].text = 'The Whole Tribe!';
</script>

Hold on there!  We've done image arrays before but what is the text property of all of these images?  Well, one of the cool things about JavaScript is that we can add properties to objects on the fly.  We simply declare the property like any other property and assign a value to it.  The result is that not only do our images have a src property (and all the other properties that images have) but they also now possess a text property.  We'll make use of the text property to add captions to our slides.

Now we'll need to progress through all of the images, one at a time, and swap out the image inside our <img> element.  We'll keep track of what slide we're on and loop back when we reach the end of our show.  Create this function inside your <script> element:

var slideNum = 0;
function rotate(){
if(slideNum>slideArray.length-1)
slideNum = 0;
document['slide'].src = slideArray[slideNum].src;
slideNum++;
setTimeout('rotate()', 3000);
}

Let's take a closer look.  First we declare a variable called slideNum to keep track of which slide we're on.  Initially, we are on slide 1 but that corresponds to array index 0 so we set slideNum equal to 0.  Next we declare the function rotate().  Inside the rotate() function, we first determine if slideNum is greater than the length of our array subtracted by one (remember, the length property indicates the number of slides, not the highest index).  If slideNum is greater than length - 1, we set slideNum equal to 0.  That takes care of the looping back to the first slide.  The next line finds the src property of the <img> element that we'll swap and sets it to the src of whichever slide we're on.  After that, we increment slideNum by one so that the next time we run rotate we'll be on the next slide.

Finally, we make use of the setTimeout() method.  setTimeout() takes two parameters: the JavaScript code to run and the number of milliseconds to wait before running the code.  In our case, we want to run our rotate() function over again with the new value of slideNum.  I've chosen a 3 second interval between slides but that's arbitrary - it could have been any number.

We should examine the logic of this a little more closely.  First, rotate() is what is called a recursive function.  That is, the function includes a call to itself within the body of the function.  Now let's examine what is actually going on here.  slideNum starts out life being set to 0.  Inside the rotate() function, first we check whether or not the value of slideNum is greater than the highest index in our array (in this case 3).  For now it is not, so the line that sets slideNum equal to 0 is skipped.  Next we set the <img> element's src to the src of slideArray[slideNum] element - in this case that's slideArray[0].  Next we increment slideNum by one so slideNum now equals 1.  We wait three seconds and call rotate() again - only this time slideNum equals 1.  That's what moves us to the next slide!  We'll work our way through all of the slides until we run through slideArray[3].  When we run rotate() with slideNum equal to 3, we display the third slide and increment slideNum to 4.  We'll then run rotate() again with slideNum equal to 4.  This time through, slideNum doesn't pass our test in the if statement, so slideNum is set equal to 0 again and the process starts over.  There's our loop!

Let's put the finishing touches on our slide show by including a caption for each slide.  Here is the finished code for the entire project:

<html>
<head>
<title>slideshow</title>

<script language="JavaScript">
slideArray = new Array();
slideArray[0] = new Image();
slideArray[0].src = 'images/madison.jpg';
slideArray[0].text = 'Madison';
slideArray[1] = new Image();
slideArray[1].src = 'images/carson.jpg';
slideArray[1].text = 'Carson';
slideArray[2] = new Image();
slideArray[2].src = 'images/hayden.jpg';
slideArray[2].text = 'Hayden';
slideArray[3] = new Image();
slideArray[3].src = 'images/kids.jpg';
slideArray[3].text = 'The Whole Tribe!';

var slideNum = 0;
function rotate(){
if(slideNum>slideArray.length-1)
slideNum = 0;
document['slide'].src = slideArray[slideNum].src;
document['form1'].caption.value = slideArray[slideNum].text;
slideNum++;
setTimeout('rotate()', 3000);
}
</script>
</head>

<body onLoad="rotate()">
<p><img name="slide" border="0" src="images/madison.jpg" width="230" height="226"></p>
<form name="form1">
<p><input type="text" name="caption" size="20"></p>
</form>
</body>
</html>

The new things to notice are the addition of an edit box to hold our captions inside a form which is inside the <body> element.  Then, inside the rotate() function, we simply assign the text property of each image to the value property of the edit box.  Finally, we attach the rotate() function to the onLoad event in the <body> element so that our slide show starts as soon as the page is finished loading.  You can see it in action on the Source Code page.

The AdRotator

Our slide show was fun but it didn't really have any practical value.  Let's see if we can create something more useful: a slide show that rotates advertisements.  In this slide show, we'll display some banner ads that are clickable - when the user clicks on an ad, they will be sent off to the website of the current ad displayed in a window that we'll create.

As you might imagine, we'll need some way to keep track of the URLs for all of our ads.  Looks like a perfect time for a second array!  In the second array, we'll "line up" the URLs much like we did in the JumpList project.  Here are the images for the project.  Save them inside an images folder just as you did for the slide show project.

java.gif

networksolutions.gif

oracle.gif

photoshop.gif

w3c.gif

Let's put the whole thing together this time and we'll discuss the parts that are different from the slide show project.  Here's the code:

<html>
<head>
<title>adRotator</title>

<script language="JavaScript">
var boardArray= new Array();
boardArray[0] = new Image();
boardArray[0].src = 'images/w3c.gif';
boardArray[1] = new Image();
boardArray[1].src = 'images/java.gif';
boardArray[2] = new Image();
boardArray[2].src = 'images/networksolutions.gif';
boardArray[3] = new Image();
boardArray[3].src = 'images/photoshop.gif';
boardArray[4] = new Image();
boardArray[4].src = 'images/oracle.gif';

var urlArray = new Array();
urlArray[0] = 'http://www.w3c.org';
urlArray[1] = 'http://www.javasoft.com';
urlArray[2] = 'http://www.networksolutions.com';
urlArray[3] = 'http://www.adobe.com/products/photoshop';
urlArray[4] = 'http://www.oracle.com';

var boardNum = 0;
function rotate(){
if(boardNum>boardArray.length-1)
boardNum = 0;
document['adBoard'].src = boardArray[boardNum].src;
boardNum++;
setTimeout('rotate()', 3000);
}

function go(){
window.open(urlArray[boardNum-1], '', 'width=640,height=480,scrollbars=yes');
}
</script>
</head>

<body onLoad="rotate()">
<p><a href="javascript:go()">
<img name="adBoard" border="0" src="images/w3c.gif" width="244" height="49">
</a></p>
</body>
</html>

In this project, the boardArray is analogous to the slideArray.  We won't need the text property as we hope that the images are self explanatory!  (By the way, wouldn't it be cool to actually have a site where all of these sponsors advertised!?!).  The urlArray is new but it closely mirrors the construct of our JumpList project: each element corresponds to an element in the boardArray.  The rotate() function is also nearly identical except that it omits setting the caption of our images.  The new function to take a look at is the go() function.  Here is where we'll open a window containing the URL of the ad on which the user clicked.  We then use the javascript pseudo protocol to assign the function to the href attribute of the <a> element.  In this case, since these are advertisements, it makes sense to open the URLs in a new window as we wouldn't want our users to forget about us!

But why is it boardNum - 1 and not just boardNum?  Well, if we examine the rotate() function closely, we'll see that as soon as we increment boardNum, it always represents one more than the current slide's index.  Think about it: boardNum equals 0 the first time through.  That means we display boardArray[0] for 3 seconds.  The next line is boardNum++ so, although we are displaying boardArray[0], boardNum is equal to 1.  In order to get the URLs aligned, we need to subtract 1 from boardNum to point to the current ad's index.  It almost makes sense!  You can see this project in action on the Source Code page.

We've made limited use of the setTimeout() method in the two slide show projects.  The real power of setTimeout() is the fact that we can run ANY JavaScript code whenever we want.  When we couple that with all of the other JavaScript that we know, it becomes a great tool.  As an example, consider a redirection scenario.  Suppose you own 2 domain names, mydomain1.com and mydomain2.com, that you'd like to point to the same website.  Rather than keep duplicates of all the pages in both locations, you could set up a redirect on the index page of mydomain2 that points to mydomain1 like this:

<body onLoad="setTimeout('window.location.href = http://www.mydomain1.com',0)">

This forces the browser to navigate to the index page of mydomain1 instantly (after 0 milliseconds).  Then, you could keep all of the files necessary for for both websites at mydomain1 and never have to worry about it!