File Under: JavaScript, Programming

Advanced JavaScript Tutorial – Lesson 3

As we learn more and more JavaScript, we can build increasingly complex applications. In the previous lesson, we added long-term memory to our JavaScripts using cookies. Now we’ll add a sense of time to our growing stock of JavaScript knowledge.


Contents

  1. Timing Events and Browser Differentiation
    1. How to Time Events
    2. Timing Loops – What They Are
    3. Timing Loops – How to Do Them
  2. How to Cancel a setTimeout
    1. A JavaScript Clock
    2. Using Timers with Variables
    3. Browser Detection
    4. How to Detect Browsers
    5. Object and Method Detection

Timing Events and Browser Differentiation

In JavaScript, the process of moving something around the screen usually involves a loop, where the element is moved little by little over time. The question is, how do you tell JavaScript to “move that piece over a little bit every 10th of a second or so”?

Unfortunately, this business of moving elements around is where the assortment of browsers differs the most. Almost everything I’ve covered up until now (except image swaps) works in basically all JavaScript-ready browsers. With dynamic HTML, however, you have to take special measures to make your scripts work across all the different browsers. So after I cover timing events, I’ll talk about ways to detect the type of browser a visitor is using (allowing you to serve them appropriate content). Luckily these two skills – timing events and detecting browser type – will also be useful when it comes to other neat-o features of the newer browsers.

Let’s start with timing events on your pages.

How to Time Events

It’s easy to time events in JavaScript. The key commands are the setTimeout() and clearTimeout() methods. With setTimeout(), JavaScript executes a certain command some time in the future. And if you change your mind, clearTimeout() cancels setTimeout.What follows is the basic format of a setTimeout.

 var the_timeout = setTimeout("some javascript statement",

                   some_number_of_milliseconds);


Here’s an example:

 var the_timeout = setTimeout("alertAndRedirect();",3000);

There are three important things to notice about this statement:

setTimeout returns a value.
In this statement, the_timeout is a variable that keeps track of this specific setTimeout. If you ever want to cancel this specific setTimeout, you can refer to it using the variable. We’ll see an example of this later. And, of course, as is true for all variables, it doesn’t have to be called the_timeout. It can be called “Frankenstein” for all JavaScript cares.
The first parameter to setTimeout is a string that contains a JavaScript statement.
In this example, the first parameter is the string:"alertAndRedirect();"alertAndRedirect is just a function I wrote that launches an alert box and redirects to this page when the user clicks “OK.” Note that the stuff in quotes is a complete JavaScript statement, with a semicolon and everything. If you executed only this statement, it would call the alertAndRedirect function. The setTimeout merely schedules that statement to occur at a specific time.If you want to see how it works, here’s the alertAndRedirect() function:
 function alertAndRedirect()

 {

 	alert('ok!  exhale!');

 	window.location.replace("timing.htm");

 }

The second parameter of setTimeout indicates how many milliseconds from now you want to execute the first parameter.
There are 1,000 milliseconds in a second. So, to have something happen three seconds from now, the second parameter has to be 3,000 milliseconds. JavaScript is told by setTimeout to “call this statement x many milliseconds from now.” Remember that, and you’ll be fine. To make sure you know what’s going on, here’s a little exercise: Make a button that tells you when three seconds, six seconds, and nine seconds have passed. Like this one:When you’ve figured it out, or gotten sick of trying, take a look at my solution to the timer exercise:

Click here.

Timing Loops – What They Are

If you’re like me, this exercise threw you at first (and if it came naturally, feel free to pat yourself on the back – you deserve it). Here’s the right way to do it:have the button’s onClick call the following function:

 function ringBell()

 {

    var timer1 = setTimeout("window.document.the_form.the_text.value='3 seconds!';",3000);

    var timer2 = setTimeout("window.document.the_form.the_text.value='6 seconds!';",6000);

    var timer3 = setTimeout("window.document.the_form.the_text.value='9 seconds!';",9000);

 }

It says, “write ’3 seconds’ three seconds from now, ’6 seconds’ six seconds from now, and ’9 seconds’ nine seconds from now.” Makes sense, right? However, the following doesn’t work:

 function doDumbTimer()

 {

    var timer1 = setTimeout("window.document.the_form.the_text.value='3 seconds!';",3000);

    var timer2 = setTimeout("window.document.the_form.the_text.value='6 seconds!';",3000);

    var timer3 = setTimeout("window.document.the_form.the_text.value='9 seconds!';",3000);

 }

 

See what happens when you try this faulty timer code? Notice that if you wait three seconds, one of the three timing messages mysteriously appears in the text box and then just stays there. In the bad code above, each of the setTimeouts get executed consecutively, (i.e., it’s saying, “write ’3 seconds’ three seconds from now, ’6 seconds’ three seconds from now, and ’9 seconds’ three seconds from now”). So after three seconds pass, all three things happen, and you end up with whichever one happens to fire last – definitely not what you want.Once you understand it, setTimeout() is quite easy to use. However, one thorny question does arise:How can you make a timer that does something every two seconds, from now until forever? For example:

Click here.

Don’t worry about the stop timer button for now, I’ll cover clearTimeouts in a bit. Just think about how you would get this timer to loop indefinitely. It’s actually a very important question, and not just a silly exercise. As I mentioned earlier, when you want to make stuff move slowly across the screen using dynamic HTML, execute a small timed loop: “Move it over a little, wait, move it more, wait … and so on.” Are you thinking about it? Well, the answer’s not an easy one. You can’t just have a function, like the one above, that changes the text box every two seconds, like so:

 function theTimer()

 {

    var timer1 = setTimeout("changeTextBoxTo(2);",2000);

    var timer2 = setTimeout("changeTextBoxTo(4);",4000);

    var timer3 = setTimeout("changeTextBoxTo(6);",6000);

    var timer4 = setTimeout("changeTextBoxTo(8);",8000);

    var timer5 = setTimeout("changeTextBoxTo(10);",10000);

    .

    .

    .



 }

 

Because, well, you can see why not: If you want something to loop infinitely, and you used this method, you’d have to have infinite lines of code. Among other problems, like serious carpal tunnel syndrome, it would take a really long time to download a page with an never-ending number of JavaScript lines, so that’s not really an option. This doesn’t work either, even though it looks sort of cool:

 function theTimer()

 {

 	the_time = 0;

 	hellIsHot = true;



 	while (hellIsHot == true)

 	{

 	  the_time += 2;

 	  var timer = setTimeout("changeTextBoxTo(the_time);", the_time*1000);

 	}



 }

 

Study this thing for a while to see what I’m getting at. But don’t try running it. The results will make you very unhappy. Let’s do a few iterations of the “while” loop:

Iteration 1
  • while (hellIsHot == true) :Yup, hell sure is hot.
  • the_time += 2 :so now the_time = 2
  • var time = setTimeout(“changeTextBoxTo(2);”, 2000) :so, two seconds from now, the textbox will change to “2.” That’s what we want.
Iteration 2
  • while (hellIsHot == true) :Verily, hell is still hot.
  • the_time += 2 :so now the_time = 4
  • var time = setTimeout(“changeTextBoxTo(4);”, 4000) :so, four seconds from now, the textbox will change to “4.” OK, that’s good.
Iteration 3
  • while (hellIsHot == true) :Nope, hell isn’t getting any colder.
  • the_time += 2 :so now the_time = 6
  • var time = setTimeout(“changeTextBoxTo(6);”, 6000) :so, six seconds from now, the textbox will change to “6.” Fine.
Iteration 4
  • while (hellIsHot == true) :OK already! It’s hot!
  • yadda
  • yadda yadda

You get the picture. It looks like this code should do the right thing. Unfortunately, it doesn’t. Instead it creates this unending loop, scheduling setTimeouts until hell freezes over. There are two problems here. First, while it’s in the loop, your browser can’t do anything else. It’ll basically freeze, spinning its wheels, scheduling setTimeouts forever. Second, every time a setTimeout is scheduled, the browser has to remember what you’re scheduling and when you want it to run. Eventually your browser will run out of memory. When this happens, your browser will crash, or your computer will crash, or you’ll get mad and never write another line of JavaScript again. Not good at all. Fortunately, there is a way to write a successful looping timer.

Timing Loops – How to Do Them

To get a timer working in a loop, you have to write a function that calls itself. Happily, this is possible in JavaScript. Here’s an example:

 var the_count = 0;

 var the_timeout;

 function doTimer()

 {

 	window.document.timer_form.the_text.value = the_count;

 	the_count += 2;

 	the_timeout = setTimeout("doTimer();", 2000);

 }

This is the timer that was used on the previous page. When a user clicks on the button, it calls this function, which in turn writes the current value of the_count to the textbox. It then increments the_count by two, and then calls itself in a setTimeout. In two seconds, the setTimeout is executed, cleared, and the function runs again. It changes the value in the textbox, increments the_count, and again uses setTimeout to call itself in two seconds. During those two seconds, the browser just sits around, doing the things that browsers do. When two seconds pass, the function runs again. And another setTimeout() is set once the_count is incremented by two. You don’t run out of memory because there’s only one setTimeout() running at any given time.

This works, whereas the infinite hell-freezing-over loop on the last page didn’t, because infinite “while” loops lock the browser up and prevent anything from happening during the execution of the loop. This setTimeout trick makes sort of a slow loop. In the above example, the browser has two seconds to do things between each iteration.

How to Cancel a setTimeout

Now that you know how to make a timer loop run infinitely, you’ll probably want to know how to stop it. This is the magic of clearTimeout. If you look at the HTML of the timer on the previous page, you’ll see the following form element:

 <input type="button" value="stop timer"

  onClick="clearTimeout(the_timeout);">

This is the button you hit to stop the timer. The command to stop a setTimeout is clearTimeout(), which is actually quite simple. When you set a setTimeout like this,

 the_timeout = setTimeout("some javascript",3000);

you can cancel it like this:

 clearTimeout(the_timeout);



Simple, no? Now let’s take a look at a slightly more complicated looping timer:one that actually tells time!

A JavaScript Clock

Click here

This clock reads the time off your computer and then updates the textbox every half second. I’m showing you this for two reasons. First, it’s another example of making a timer by having a function call itself. Second, it shows you a little more about the Date object, which I mentioned back when we covered cookies.

Here’s the code:

 function writeTime() {



   // get a date object

   var today = new Date();



   // ask the object for some information

   var hours = today.getHours();

   var minutes = today.getMinutes();

   var seconds = today.getSeconds();



   // fixTime makes the minutes and seconds look right

   // it just sticks a zero in front of numbers less than 10

   minutes = fixTime(minutes);

   seconds = fixTime(seconds);



   // put together the time string and write it out

   var the_time = hours + ":" + minutes + ":" + seconds;

   window.document.the_form.the_text.value = the_time;



   // run this function again in half a second

   the_timeout= setTimeout('writeTime();',500);



 }



 function fixTime(the_time) {



 	if (the_time <10)

 	{

 		the_time = "0" + the_time;

 	}

 	return the_time;

 }

It might look a little long, but that’s just because it’s well commented. Let’s go through the code line by line.

var today = new Date();
Just as new Array() creates a new Array object for you to fill, new Date() creates a new Date object. Once you have the object, you can ask it questions about itself. When you create a Date object with nothing between the parentheses, as we’ve done, JavaScript looks at the computer’s clock and uses it to create a new Date object. Now that we have a Date object, called “today,” we can get information from it.
var hours = today.getHours();
This is how to get the current hour. It’s military time, so if it’s 2 p.m. when you get the new Date(), getHours() will return “14.” Understand that getHours() is a built-in method call of the JavaScript Date object. As is the case with most JavaScript objects, the way to know when you should use getHours is by checking with a good JavaScript reference book.
var minutes = today.getMinutes(); var seconds = today.getSeconds();
These lines are just like getHours();.
minutes = fixTime(minutes);
One problem with getMinutes is that if it’s 11:01, getMinutes will return a “1.” This is fine unless you want to make a clock like we do, in which case, we want the “1″ to be written as “01.” That’s what the fixTime function that I wrote does. It’s a pretty straightforward function, so I’m not going to describe it. Just take a quick look at it to see what it’s doing.
The next two lines put the string together and write it to the form element.
We’ve seen this sort of nonsense a million times.
the_timeout = setTimeout('writeTime();', 500);
This is the line that makes the loop. It says “in half a second, call the function again.” When the function gets called again, it does another today = new Date(), which sends it to the computer’s clock again. The interval could have been every second, but doing it every half second helps keep the time more accurate.

OK, enough of the looping timer trick. There’s one more thing you should know about timers:how to use them with variables.

Using Timers with Variables

Like I mentioned earlier, setting a setTimeout is like marking a time in the future for JavaScript to execute a statement. This is fine and dandy when your setTimeout is something like this:

 var the_timeout = setTimeout("alert('Hello!');", 60000);

Here you’re telling JavaScript to wait one minute and then throw up a “Hello!” alert box. But what if you want to do something like this:

 var the_string = "hello";

 var the_timeout = setTimeout("alert(the_string);", 60000);

This tells JavaScript to wait one minute and throw up an alert box that contains whatever variable provided by the_string. So after a minute, JavaScript looks for a variable called the_string and calls alert() with that variable. The problem? In a minute’s time, the_string might contain something totally different from “Hello!” In fact, if you had the above two lines of JavaScript inside a function, the setTimeout would probably give you an error. For instance, if you had a function like this:

 function alertInAMinute()

 {

     var the_string = "hello";

     var the_timeout = setTimeout("alert(the_string);", 60000);

 }

And then you called the function inside a link like so:

 <a href="#" onClick="alertInAMinute(); return false;">blah!</a>

This would probably produce an error because you defined the variable called the_string inside the function using var. Remember:When you use var inside a function, as far as JavaScript is concerned, it exists only inside that function. This function creates a variable called the_string, sets the setTimeout, then exits. Because you used var, the variable called the_string is erased from JavaScript’s memory. And then, when JavaScript looks for the_string in its memory a moment later, it’s nowhere to be found, and you are dished up an error.

This problem occurs because you’re passing a variable to setTimeout. Eliminate the problem by passing it the value of the variable instead of the variable itself. Here’s how:

 function alertInAMinute()

 {

     var the_string = "hello";

     var the_timeout = setTimeout("alert(" + the_string + ");",60000);

 }

This code pulls the the_string variable out from inside the quotes of setTimeout. And because the variable isn’t in quotes, JavaScript puts in the value of the variable.

You may be asking why I bothered with a variable in the first place. Well, I’m simplifying things to make a point. When you tackle today’s homework, you’ll see where this really makes a difference.

OK, enough of timers. As I said earlier, timers are used all over the place in dynamic HTML, so they’re worth learning. But don’t get too swept up in multimedia merriment – to make all this safe for the teeming millions who can’t see dynamic HTML, you need to know how to detect what browsers your visitors are using.

Browser Detection

This section (obviously) needs to be updated.

Almost everything I’ve covered so far works on all JavaScript-enabled browsers. However, there are some things that just don’t fly on certain browsers. MSIE 3.0, for one, doesn’t support image swapping. So if you have a page that depends on image swapping to function correctly, either you nix the image swapping altogether, or you furnish MSIE 3.0 visitors with information tailor-made for their browser.

There are a few approaches to dealing with browsers with different capabilities. The most straightforward method is to determine what browser someone is using and then serve up the appropriate browser-specific information.

There are several ways to do this. Some sites ask you to choose which browser you’re using right up front; e.g., “Click here if you’re using Netscape.” But this isn’t an ideal solution for a number of reasons. For one thing, it’s a hassle for the user (and, with impatient users, that extra click may mean the difference between staying or backing out). It also means that you have to maintain duplicate (or maybe even more) versions of the same pages.

An alternative is to use JavaScript to figure out what browser someone is using, and automatically route the visitor to a set of pages that works for that browser. Unfortunately, this still forces you to maintain two or more versions of each page, which is really a drag.

A much better solution is to make your pages smart enough to look one way to some browsers and another way to other browsers (which is what we’ll concentrate on for the rest of today’s lesson).

How to Detect Browsers

This section (obviously) needs to be updated.

Along with the Document object, each browser window has a Navigator object, which has two properties containing all the information about the sort of browser you’re using. These are appName and appVersion.

The application name is appName. If you’re using a version of Netscape, and you have this line in your JavaScript:

 var the_browser_name = navigator.appName;

Then the_browser_name will be “Netscape.” For MSIE, the_browser_name would be “Microsoft Internet Explorer” (each browser company comes up with its own name). So, if you just want to know whether someone’s using Netscape or MSIE, use appName.

More information is stored in the appVersion. A sample appVersion is “4.03 [en] (Win95; I),” which is the browser I’m using as I type this very tutorial. It’s Netscape version 4.03, English International, for Win95/NT. Whatever. Beyond the version number, you probably won’t need all that information. Happily, getting the version number out of this monster string is quite easy:You just grab the first number. The quickest way to do this is with a function called parseFloat(), which pulls the first thing that looks like a decimal number out of the string and returns it:

 var the_browser_version = navigator.appVersion;

 var the_version_number = parseFloat(the_browser_version);

Unfortunately, this little trick won’t work for Internet Explorer 5.0 or later because Microsoft decided to start their appVersion string with the numbers 4.0. Why did they do this? To irk us all! Luckily, except for some crazy IE5 specific stuff, JavaScript is the same in IE5 and IE4, so in general it’s ok for your scripts to be a bit misinformed. If you REALLY want to be sure you know what browser someone is using, refer to Dr. Richard Blaylock’s browser detector library.

OK, let’s do something useful with this information. Click the button below to see an incredibly straightforward use of the Navigator object.

Here are the two relevant functions. Clicking on the button calls browserSizeUp():

 function browserSizeUp()



 {

    var browser = navigator.appName;

    var version = versionNumber();



    if ((browser == "Netscape" ||

         browser == "Microsoft Internet Explorer") && (version >= 4))



    {

    alert("The browser doctor says:Now that's a pretty fancy browser

           you got there!");



    } else {



    alert("The browser doctor says:Hmm. Maybe it's time to upgrade.");



    }



 }



 function versionNumber() {



   // return version number (e.g., 4.03)



   return parseFloat(navigator.appVersion)

 }

What’s happening? If we like the user’s browser, we give one alert. We dish up a different alert if we think he or she needs to get modern. In a real-life application, you’d use document.writeln() to generate HTML that’s suitable for different browsers.

What users see depends on which browser their using. Unfortunately, with all the different browsers, browser versions, and platforms out there, it’s nearly impossible to keep track of which browsers support what features. And it’s only going to get worse as the number of browsers grows and the number of features you have to keep track of continues to increase.

Luckily, there’s a way to preserve your sanity. Read on and learn about object and method detection.

Object and Method Detection

Ultimately, your best approach is to determine what feature you need for any given bit of JavaScript code, and then make sure that the browser has that feature.

For example, we know that some browsers can do image swaps and others can’t. Good thing this ability is easy to detect:just make sure that the browser has the document.images object:

 if (document.images)

 {

 	do lots of image swap stuff

 }

This makes sure that your image-swap code will be run only by browsers that support image swaps. If the document.images object doesn’t exist, the if test fails and the code doesn’t run.

This sort of trick works for methods and functions, too. Not all browsers have every method. For example, in Netscape 4 you can actually move windows around the screen using the window.moveTo() method. If you want to check to see if that method exists before you invoke it, do this:


 if (window.moveTo)

 {

 	make that window fly

 }



By making sure the browser supports the feature you want to use before you invoke it, you ensure that your script will work without any obnoxious script errors. By employing a feature-checking strategy, rather than a browser-checking one, you don’t have to keep track of the capabilities of every browser in every version on every platform.

OK, that’s all about browser detection. But while we’re on the topic of browsers, let’s take a look at another browser-centered object: the History object.

If you look at the JavaScript Object Hierarchy, you’ll see we’ve touched on all but two of the top-level objects: Packages and History. Packages will have to wait for a Java tutorial, but History is JavaScript specific and very useful.

History contains the list of URLs that your browser has visited. Every browser has a way to call up a list of the pages you’ve visited recently. Clicking and holding down on your browsers back button generally does the trick. The History object lets you jump backward and forward in this list using the window.history.back() and window.history.forward() methods. The back link should act just like the back button on your browser.

These two methods are most useful inside frames, where the browser’s back button tends to take you to unexpected pages. It also makes a good addition to our ever-growing Webmonkey browser, which (surprise, surprise) is part of today’s homework. But first, let’s review. Today we covered two things that will help you move from old-tyme static HTML to the newfangled world of dynamic HTML: timing events and methods of dealing with browser incompatibilities. With the JavaScript you know now, learning dynamic HTML will be a snap.

Tomorrow, we’ll end our coverage of JavaScript objects and syntax. But don’t be too sad — this isn’t the end but the beginning. True JavaScript knowledge comes only from extensive practice and hacking. By the end of tomorrow, you’ll be ready to confidently head out into the wild world of JavaScript to hone your skills with endless coding.

We’ll also cover the finer points of the use of JavaScript with images, including JavaScript in client-side image maps and preloading images to make your image swaps work quickly (or just work). After that, we’ll take a long excursion into the world of object-oriented programming. You know about Date objects and Window objects already. Tomorrow we’ll learn how to create our own objects and how to access and control them in several different ways.

Before going onto the Next, you might want to try this homework assignment to solidify your understanding of the JavaScript objects we already know.