0753 Javascript For Beginners
0753 Javascript For Beginners
Permission is granted to copy, distribute and/or modify this document under the terms of the
GNU Free Documentation License, Version 1.1. A copy of the license is included in the section
entitled “GNU Free Documentation License”
JavaScript allows you to create ‘dynamic, interactive’ web pages: web pages that do things in
response to what your readers do, without having a separate page for each possible response. In
this tutorial, I’m going to cover some of the very basics of using JavaScript in your web pages.
I’m going to assume that you know a little bit about HTML, but nothing about programming or
JavaScript. If you aren’t familiar with HTML, you’ll want to go over the Web Writing Guide at
http://www.hoboes.com/NetLife/Web_Writing/ first.
First, JavaScript has nothing to do with Java. Java is a separate programming language.
JavaScript is a language for scripting web browsers and web servers, although it is seeing other
uses as it becomes popular. Java was created by Sun and is designed for complete software
applications, while JavaScript was created separately by Netscape Corporation and was later
standardized as the ECMAScript standard. It is designed for modifying (and “scripting”) web
pages.
JavaScript turns every web page into a collection of objects that you can modify and interact
with. JavaScript turns everything that the visitor does into events that you can modify and
intercept.
Go to http://www.hoboes.com/NetLife/Web_Scripting/Web_Scripting_Resources/ to download
the resource zip archive and uncompress it. Open the file “1941.html” in your favorite text editor.
There are several links on that page; some of the links are local (they go to hoboes.com) and
some of them are not local (they go to Wikipedia and Amazon). We’re going to make the non-
local ones open in a new window (or tab, depending on the visitor’s preferences).
Look in the HTML’s “head” area for a comment that says “put your scripts.js here”. Underneath
that line, add this:
<script type="text/javascript" src="scripts.js"></script>
This line tells the browser to look for some scripts in the file “scripts.js”. We’re going to put our
JavaScript code there. The syntax for the <script> tag is very similar to the syntax for the <img>
tag.
The <script> tag can go anywhere in your document, but you’ll usually put it in the <head> area
when you’re using it to include a file containing JavaScript. We’ll use it for other purposes later
in this tutorial.
document.onclick=manageLinks;
Save scripts.js into the same folder as 1941.html. Reload 1941.html in your browser. Try clicking
on some of the links: none of them should work.
If the links do work, you’re going to need to track down the error. In Firefox or in Safari, you
have an error console that will display any errors in your page, as well as the line those errors
occurred on. If you’re not already using one of those browsers, you’ll need to download the latest
version and use it for testing your scripts.
4—Intercepting Clicks
Functions
What do these four lines do? The first three lines create a “function”. A function is a collection of
scripting lines that you can call by name elsewhere in your script file and anywhere on your web
page. We’ll be creating a lot of functions over the course of this tutorial. This function has the
name “manageLinks”. The lines of a function are contained between the opening curly bracket
and the closing curly bracket.
Functions often accept “parameters” or “arguments”. The manageLinks function accepts one
parameter, which it names “event”.
Functions often return data to whoever called the function. The manageLinks function always
returns “false”. That’s its only purpose for the moment: it is its only line. All JavaScript lines that
do something, such as the “return” line, end in a semicolon.
So, what happens when the visitor clicks on a link on the web page? The link itself is an object,
usually an HTMLAnchorElement (we’ll deal with that sort of thing later). Whatever object they
clicked on generates an event, in this case an onclick, and starts bubbling that event up through
the document. If the link is contained in a paragraph element, the onclick bubbles up through the
paragraph. If the paragraph is contained in a body element, the onclick bubbles up through the
body, and so on, until it hits the topmost parent of all of the elements in the web page, the
document.
Once the onclick event hits the document, we’ve told document that all onclicks need to call
manageLinks.
The manageLinks function always returns false. That tells the document not to do anything else.
If we returned nothing, or if we returned true, the document would know to continue doing what
it otherwise would have done. You can try that now. Change the ‘false’ to ‘true’, reload the web
page, and try clicking some links. Now they’ll work.
return false;
}
Save scripts.js and reload the web page, and now when you click on a link an alert box will pop
up telling you what the URL of that link is.
Properties
In JavaScript, objects have properties. The “event” that the browser sends the manageLinks
function has a property called “target”. That target is the HTML element that the visitor clicked
on. HTML elements are themselves objects with properties, and the <a> tag has the property
“href” that corresponds to the “href” attribute in the HTML.
To get at the properties of an object, use a period and then the property name after the object’s
name. The target of the event object is event.target; the href of the link object is link.href.
Variables
In scripting languages we often use variables to store data for later use. Here, we’re using the
variable “link” to store the HTML element that the visitor clicked on, and we’re using the
variable “url” to store the URL that that HTML element has in its href attribute. Later in the
script, we use the “url” variable as an alert message.
When you create a variable inside of a function, you’ll want to put the keyword “var” in front of
that line. If you don’t do that, the variable becomes “global” and is accessible in any other
function, as well as throughout your web page. That might sound useful—and occasionally it
is—but it also makes it very easy to confound two supposedly different variables that happen to
have the same name. Keep your variables safe by using the “var” statement to create them.
Variables in JavaScript are case sensitive. URL and url are different variables. It’s generally a
good idea to avoid confusing variable names (such as having both the variables URL and url in
the same script). You should name your variables in a way that reminds you of what the variable
contains. Often, your variable names will contain more than one word. You can use capital letters
or underscores to mark the beginning of each new word. For example, manageLinks might
instead have been named manage_links.
6—Intercepting Clicks
Methods
Objects in JavaScript also contain methods. Methods do something. The window, for example,
has a method called “alert” that displays a message. Methods are functions that are attached to an
object. Just like functions, some of them accept parameters. The window.alert method accepts a
string of text, which it displays in the browser. You will find window.alert extremely useful when
testing your scripts.
Strings
There are different types of variables in JavaScript. One common type is the string. A string is
just a collection—a string—of text. In the latest version of our script, the text “The url is ” is a
string; we can tell that it’s a string because it is between quotes. The variable “url” is also a
string.
We can combine strings (“concatenate” them) using the “+”. Within the window.alert method, we
concatenate “The url is ” with the url variable.
return false;
}
Most elements are contained within other elements. What actually gets the click? The bottom-
most element. That’s important, because sometimes the <a> tag is not the bottom-most element.
Try clicking on the two images to the right and left of the headline at the top of the page. The
alert box will say that the URL for IMG is undefined, but those images are linked. The problem
is that it’s the <img> tag that is getting the click—the <a> tag surrounds the <img> tag but the
<img> tag is the bottom-most tag. The URL is stored on the <a> tag. If we’re going to do
something special with the URL, we need to find the <a> tag first.
return false;
}
Now, whenever you click on a link, the alert will tell you what URL applies to that object, even if
(as for an <img> tag) the element you clicked on does not itself contain the href.
Comments
The first new line in this version of the manageLinks function is a comment. JavaScript
completely ignores all lines that begin with two slashes. We can use this to place comments in
our script to remind us of what the script is doing. Whenever it isn’t immediately obvious what a
section of the script is doing, you should add a comment to that section describing the purpose of
those lines.
While loops
The second new line in this version of manageLinks is a while statement. Notice that the line
with the while ends in an open curly bracket, and there is a new close curly bracket two lines
below it. Just like a function, a while contains JavaScript code that only gets performed if special
conditions are met. For functions, the special condition is that you call the function by name. For
whiles, the special condition is that some condition is true. As long as that condition remains
8—Intercepting Clicks
true, the lines between the opening and closing brackets of the while will loop, performing
themselves over and over.
The condition is specified inside of the opening and closing parentheses. Here, our condition is
that “link.tagName != ‘A’”. The characters “!=” mean “is not equal to”. Let’s take a look at how
this condition is or isn’t met. If the visitor clicks on an <a> tag, the browser will check that
condition and ask “link.tagName is not equal to A; is that true or false?” In that case, the
condition is false: the visitor did click on an <a> tag, so the tagName is A.
If the visitor clicks on an <img> tag, what happens? The first time through, “link.tagName is not
equal to A” is true: link.tagName is IMG, and IMG is not A. So the while’s code is performed for
one loop. During that one loop, the variable “link” is set to its own parent node. If the <img> tag
is contained within an <a> tag, its parent will be that <a> tag. So the second time through,
“link.tagName is not equal to A” is false: link.tagName is A and A is A. The condition is false, so
the while loop ends.
The “while” loop we’ve added goes up the HTML family tree until it finds an <a> tag. What
happens if it never finds an <a> tag? Eventually, it reaches the top-most element and can’t go any
further. The <html> tag’s parentNode is undefined. JavaScript is perfectly happy to deal with
undefined values. But undefined values also have no properties, and trying to get a property that
doesn’t exist is an error. Once link becomes undefined, link.tagName is an error. We need to fix
that.
if (link) {
var url = link.href;
var tag = link.tagName;
Intercepting Clicks—9
return false;
}
Now, the script will basically act the same way to the visitor—only showing the alert if they
click on a link—but the errors will go away in the error console.
Compound conditionals
The conditional in the while parentheses is no longer just “link.tagName is not equal to A”.
There’s now a “link &&” in front of that. Double ampersands in JavaScript mean “and”. They
combine two different conditionals into one conditional. Here, the first conditional is just “link”.
When a variable stands alone as a conditional, it is “true” if it contains something, and “false” if
it contains nothing. Nothing can be zero, an empty string, or undefined. So as long as the link
variable points to a valid HTML element, the link variable will evaluate to “true”. Once it is
undefined, it will evaluate as false.
In logical statements, “false and anything” is always false: “false and true” is false, “false and
false” is also false. So once link becomes undefined, JavaScript doesn’t even bother checking the
rest of the conditional, and it never runs into the error that link.tagName doesn’t exist.
If statements
Just like functions and whiles, an if statement marks off lines of JavaScript inside curly brackets.
Just like while, the if needs a conditional between parentheses. With an if, however, the
conditional is only evaluated once. There is no looping. If the conditional is true, the if’s lines are
performed. If the conditional is false, the if’s lines are not performed.
So in this example, if the link variable is undefined, we don’t bother trying to get the href and
tagName off of it, nor do we pop up an alert box.
If you guessed that, you’re right. Let’s continue to build this script slowly. We know how to
block links, and we know how to let them work normally, so let’s block external links and allow
local links to work normally.
10—Intercepting Clicks
The first thing we need to decide is, what is an external link? When you’re programming, you
need to know the precise conditions that you want to act on. What are the conditions that make a
link external?
In this example, we’ll decide that “external” links are links that don’t go to www.hoboes.com.
Any URL that doesn’t begin with http://www.hoboes.com/ is an external link.
if (link) {
var url = link.href;
return true;
}
If all goes well, you should now be able to follow any link that leads to the www.hoboes.com
web site, but be blocked from following links that don’t go to www.hoboes.com.
String methods
Strings in JavaScript are objects just like HTML elements are. They have methods and at least
one property (length). One of the methods that strings have is indexOf. The indexOf method
accepts as its parameter a string of text. It returns the location of that string of text in itself. If the
string of text doesn’t appear anywhere, indexOf returns a negative one.
Test this using your browser. In your browser’s URL bar, type:
javascript:window.alert("hello world".indexOf("world"))
The alert box should say “6”, because “world” starts at location 6 in “hello world”. Now, type:
javascript:window.alert("hello world".indexOf("ll"))
It will come back with “2”, because “ll” starts at location 2 in “hello world”.
Notice that “w” and “l” are the seventh and third characters respectively in the text “hello
world”. JavaScript, like many programming languages, starts counting at zero rather than one.
Intercepting Clicks—11
This is a useful feature in other situations but it can be a bit confusing when working with string
indexes.
javascript:window.alert("hello world".indexOf("hello"))
Comes back with “0”. When a string begins with the string sent as a parameter to indexOf, we’ll
get back a zero. So if a URL begins with “http://www.hoboes.com/” url.indexOf('http://
www.hoboes.com/') will be zero. Otherwise, it will either be -1 or (unlikely) something greater
than 0.
And this makes the if statement return false if the link is external (linkIsExternal is true).
We already know that if the function returns false, it blocks the visitor from following the link.
If there is no link, or if linkIsExternal is false, the if doesn’t do anything, and the script continues
on until it reaches “return true”.
That something special is to block the link, but we don’t want to block it, we just want to open it
in a different window.
We’ve been using window.alert without talking about it. The window is an object just like the
document is an object. The document is the web page; the window is the browser window that
contains the web page. One of the methods that the window has is the method to display an alert
box. Another method is a method to open a new window.
The window.open method accepts two parameters: the URL to go to, and the name of the
window it will open in. If a window of that name already exists, window.open uses it; otherwise,
it creates a new window with that name. This helps keep from filling the visitor’s screen with
browser windows.
DANGEROUS LINKS
Be careful how you use this knowledge. Designers love restricting their visitors, constraining
their ability to leave the site and go somewhere else, restricting their ability to navigate except in
predesigned paths. For the most part, you don’t need to open new windows for your visitors,
because they can do that themselves if they want it.
Suppose, however, that there are some links we’ve categorized as dangerous. Maybe we’re
writing about web viruses, and we’re giving some examples of pages that have been infected in
the past. Or we’re writing about libel, and we’re giving some examples of pages that have
libeled. For those links—and only those links—perhaps it makes sense to pop up a confirmation
box reminding the visitor that they should be careful following this link.
The first step is to determine what makes a link dangerous. What criteria can we use to detect
that we’ve categorized this link as dangerous but not some other link?
Most likely, if we’re categorizing links we’re also putting CSS classes on those links. For
example, we might have a CSS class named “dangerous” that gets applied to dangerous links.
JavaScript can check for that.
Go into the HTML for 1941.html and categorize three links as “dangerous”. Add
“class="dangerous"” to the <a> tag for them. I recommend using the links in the link list (“<div
id="links">”) towards the bottom of the page.
if (link) {
Intercepting Clicks—13
if (link.className == "dangerous") {
var confirmation = window.confirm(link.text + " might be dangerous. Are you
sure you want to follow this link?");
return confirmation;
}
}
return true;
}
We can access the class attribute of an <a> tag using the className property. (The word “class”
in JavaScript is reserved for other purposes.) The conditional “==” is the opposite of “!=”. It
means “is equal to”. This conditional reads “if link.className is equal to ‘dangerous’ then…”.
One of the methods that windows have is the “confirm” method. It’s like an alert box, except that
it provides both a “cancel” and an “OK” (or equivalent) button. If the visitor chooses “cancel”,
window.confirm returns false. If the visitor chooses “OK”, window.confirm returns true. This
matches what we return to tell the browser to follow or not follow the link, so we can just go
ahead and return the result of window.confirm.
if (link) {
if (link.className == "dangerous") {
var normalBackground = link.style.backgroundColor;
link.style.backgroundColor = "red";
var confirmation = window.confirm(link.text + " might be dangerous. Are you
sure you want to follow it?");
link.style.backgroundColor = normalBackground;
return confirmation;
}
}
return true;
}
All of the styles of an element are stored in the style property on that element; the style property
is itself an object with properties of its own. Those properties are CSS styles. The dashes are
14—Intercepting Clicks
removed, and the succeeding words capitalized. The style “background-color” becomes the
property “backgroundColor”.
In this example, we first store the existing background color, set the background color to red,
handle the confirmation, and then reset the background color to whatever it was before.
Odd change
One of the things about JavaScript is that how it works often changes, sometimes in response to
the annoying things web spammers do, and sometimes because the behavior is simply undefined.
Despite the sample image, changing the background color before displaying an alert window
doesn’t work in Safari: Safari’s behavior has changed since I wrote that part of this tutorial.
Safari waits until the script ends to update the style change, at which point we’ve changed it back
to the original. As I write this in March 2012, this code does work as expected in Chrome and
Firefox.
This is one of those cases where you have to decide if the inconsistency matters. Is the red
background color critical, or is it just something to make it easier for the visitor if the visitor
happens to be using a browser that supports it?
AN IMAGE GALLERY
One common use of web pages is to display images, with one image per page. Normally the page
doesn’t change except for the image. JavaScript can help speed things up, by not reloading
anything except for the new image.
From the resources.zip archive, open “gallery.html” in your browser. Verify that it works by
following the links to the images. Clicking on each link should open just that image in your
browser. If it doesn’t work in standard HTML, it won’t work after you add JavaScript to the mix.
Create a new, empty, script file called “gallery.js”, and in this file put:
function switchImage(link) {
window.alert(link.href);
return false;
}
This function will accept one argument: the link to the new image. For the moment, however, all
the function will do is open an alert box displaying the URL to the image.
For each <a> tag in the HTML of gallery.html, add an onclick attribute with the value “return
switchImage(this);”. For example, the first link’s line will look like this:
<li><a onclick="return switchImage(this);" href="gallery/
Ancient_Sarcophagi.jpg">Ancient Sarcophagi</a></li>
Because switchImage returns false, the browser won’t follow the link.
Events in tags
In the previous example, we put an onclick into the document object using JavaScript. Here,
we’re putting an onclick into the <a> tag using HTML.
This
When inserting JavaScript into HTML tags, “this” is always the tag that the JavaScript has been
inserted into. Here, “this” is the <a> tag object containing a link to Ancient_Sarcophagi.jpg.
16—Image Gallery
SWITCH ON CLICK
If you look in the HTML, you’ll see that the <img> tag has an ID of “frame”. When we call the
switchImage function, we want to find the element with that ID in the document.
return false;
}
Now, when you click on a link the image in the display should switch immediately to that link’s
image.
Look carefully at the method’s name: the Id portion of the name is capital-I and lowercase-d.
JavaScript functions are case sensitive, so using getElementByID, as I often do, will fail.
CAPTIONS
We now have a fully working photo gallery. What can we do to improve it? Photos often have
captions, and each of the <a> tags linking to our photos has a title attribute whose text would
make a perfect caption. So far, we’ve only modified the attributes of HTML tags. But once we
get ahold of a tag’s object, we can even modify the text that the tag surrounds. If there’s a
paragraph on the page and we want to change the text of that paragraph, we can do so—as long
as we can find that paragraph’s JavaScript object.
If the paragraph has an ID attribute, we can use getElementById to get ahold of that paragraph.
In the HTML for gallery.html, find the <img> tag with the ID “frame”. Underneath it—but still
Image Gallery—17
inside the <div> that surrounds it—add an empty paragraph with the ID “caption”:
<div>
<img id="frame" src="…" />
<p id="caption"></p>
</div>
return false;
}
Try this, and now when you click on one of the links, not only will the image change but a
caption will display beneath the image.
innerHTML
The innerHTML is the stuff between the beginning and ending of a tag. If a paragraph was
<p>Hello, world!</p> and you set the innerHTML to “Goodbye, cruel world!”, the result is as if
it were <p>Goodbye, cruel world!</p>.
String replace
As you might guess from the name, innerHTML can contain HTML as well as straight text. This
means that the innerHTML can contain <a> tags, <em> tags, and any tag that legally fits inside
the tag you’re modifying. Try replacing “captionParagraph.innerHTML = link.title” with:
var caption = link.title;
caption = caption.replace(
'William Miller',
'<a href="http://wikipedia.org/wiki/William_Miller_(engraver)">William Miller</a>'
);
captionParagraph.innerHTML = caption;
The “replace” method on a string looks for the first parameter and replaces it with the second
parameter. In this case, it looks for the text “William Miller” and it replaces it with a link to
Wikipedia’s William Miller entry.
18—Image Gallery
RANDOM IMAGE
Currently, the photo gallery only acts in response to visitor activity. That is almost always the
best way to start, and often the best way to stop as well. Let the visitor have control over what
happens in their browser. With a photo gallery, however, a slide show might be useful. What
would we need in order to make this gallery into a slide show?
1. We need a list of the images. And we have a list of images. JavaScript can access our HTML,
and our HTML contains a UL filled with one LI per image.
2. We need to be able to set actions to happen in advance. After we switch to the next image, we
don’t want to immediately switch to the image after that—the image would only be onscreen
for a fraction of a second. We need to switch to the next image, wait ten or fifteen seconds,
Image Gallery—19
The first step is to construct a list of the images and pull one of them out to display. Arrays in
JavaScript are variables just like any other, but we construct them using the Array statement. For
example:
var fruit = new Array('apple', 'banana', 'orange');
Later on, if we want the second fruit, we would use “fruit[1]”. For example,
“window.alert(fruit[1])”. Just like with strings, JavaScript starts counting arrays at zero. So if
there are three items, the items are item 0, item 1, and item 2.
We could construct our list of images in this way, listing each image in the Array statement. But
one of the most important rules of programming is DRY: Don’t Repeat Yourself. We already
have a list of the images in the HTML. If there’s some way that our script can access this list, we
don’t have to repeat ourselves.
Give the <ul> tag an ID. Call it “images”. The ul tag should look like:
<ul id="images">
Now that the UL has an ID, we can get it using getElementById. Create a new function,
“randomImage”.
function randomImage() {
var imageUL = document.getElementById("images");
var aList = imageUL.getElementsByTagName('A');
var aChoice = Math.floor(Math.random()*aList.length);
switchImage(aList[aChoice]);
}
Once you have this function, go to the URL bar of your browser and type
“javascript:randomImage()”. Most of the time, the image should change to a random image.
(Remember that since we only have six images, sometimes it will randomly choose the image we
already have.)
In the randomImage function, the parent element is the UL with the id “images”. After calling
getElementsByTagName('A') on that element, we have a list of every A tag contained within that
UL. This list is the six images in our image gallery.
20—Image Gallery
Math.floor will remove decimals from a positive number. Applying Math.floor to a random
number from 0 to the length of a list will produce a random integer from 0 to one less than the
length of the list. For our list of six A tags, it will produce a random integer between 0 and 5.
Once we have that random integer, we pull that item from aList using the square brackets. If
aChoice is 3, then aList[aChoice] is the fourth image link.
This is just like the “document.onclick” we used in the “Intercepting Clicks” example. In this
case, however, we’re telling the window that, when it is done loading, to call the randomImage
function.
Let’s go ahead and make a new function called slideShow. It will start when the window finishes
loading, and it will display each of the images in order.
function SlideShow() {
this.slides = document.getElementById("images").getElementsByTagName('A');
this.imageFrame = document.getElementById("frame");
this.captionParagraph = document.getElementById("caption");
this.currentSlide = 0;
this.advance = function() {
this.currentSlide++;
var link = this.slides[this.currentSlide];
this.imageFrame.src=link.href;
Image Gallery—21
this.captionParagraph.innerHTML = link.title;
}
}
This looks very similar to what we’ve already done, with some odd flourishes I’ll get to in a
moment. Test this out using:
javascript:gallery = new SlideShow();
javascript:gallery.advance();
Do the first line (new SlideShow) once. Do the second line as many times as you want. It should
advance one slide every time you run it.
What is this?
There is a whole bunch of “this” in this function. Previously, we used “this” to pass an A element
to another function. What “this” really means is “the current thing”, and that current thing can be
a function as well as an element. In fact, functions in JavaScript look a whole lot like objects.
They can contain properties and methods just like HTML elements can and just like the window
and the document objects do.
What we’re doing with “this” throughout this function is setting up one methods (advance), and
several properties (currentSlide, imageFrame, captionParagraph, and slides). We’ll be using
currentSlide to remember which slide was most recently displayed. We’ll use imageFrame and
captionParagraph just like we used them in the switchImage function. And we’ll use slides to
remember the list of images, so that we don’t have to recreate the list every time we cycle
through a slide.
The advance method adds one to the currentSlide property, then gets the A element using that
number. When gallery.currentSlide is 3, it will display image and caption from the third A
element.
Creating objects
I mentioned earlier that new arrays can be created using “new Array()”. Well, new objects of any
kind can be created using “new”. “gallery=new SlideShow()” creates a new SlideShow and puts
it in the variable “gallery”.
I gave SlideShow a capitalized name; that’s not required, but it is somewhat standard for naming
classes.
And note that there is no “var” in front of “gallery =”. That’s because this needs to be a global
variable. We’ll be using it from multiple places on the page by the time we’re done.
22—Image Gallery
++
There’s one more minor new thing: “++”. When a numeric variable is followed by two plus
signs, this “increments” the variable: adds one to it. Adding one to (and subtracting one from)
variables is so common that many programming languages include this abbreviated way of doing
it. It’s easier to type and see than “this.currentSlide = this.currentSlide + 1”.
The equivalent for “decrementing” a variable by one is “--”. If we wanted the slide show to go
backwards, we’d be using “this.currentSlide--”.
//advance the slide show, and set the timer for the next advance
function advanceSlide() {
gallery.advance();
window.setTimeout(advanceSlide, 5000);
}
In the URL bar, type “javascript:beginShow()” and wait 30 seconds. After 30 seconds it should
advance to the second slide, and then advance every five seconds afterwards.
Add beginShow to the windows’ onload and the show will start up 30 seconds after the page
loads.
window.onload=beginShow;
You can see in these two functions why we need gallery to be global (which is why we don’t
create it using “var gallery = …”). The gallery variable is created in one function but used in the
next. If it were created using “var”, it would only be available in the function that created it.
Timers
The window object is your doorway to the visitor’s computer—as much of a doorway as you’re
going to get. It includes a method for telling the browser to run some code at a later time. The
setTimeout method accepts a function and a number of milliseconds as its parameters. In
beginShow above, we’re telling the browser to run the advanceSlide function in 30,000
milliseconds (30 seconds). Every time it runs advanceSlide, the last thing advanceSlide does is
tell the browser to run advanceSlide in 5000 milliseconds (5 seconds).
Timers are the reason we have to put the control code in a separate function instead of in a
method on the SlideShow class. If we try to have the timer call a method on gallery, it will
happily call that method—but without any of the context of the method being part of an object. It
will lose track of “this” and apply it to the window instead of to the gallery object.
The first step is to move image selection from the separate “switchImage” function to a method
on the gallery object. Separate out the code in “advance” that switches the image into its own
method:
function SlideShow() {
this.slides = document.getElementById("images").getElementsByTagName('A');
this.imageFrame = document.getElementById("frame");
this.captionParagraph = document.getElementById("caption");
this.currentSlide = 0;
this.advance = function() {
this.currentSlide++;
if (this.currentSlide >= this.slides.length) {
this.currentSlide = 0;
}
this.displayImage(this.slides[this.currentSlide]);
}
this.displayImage = function(link) {
this.imageFrame.src=link.href;
this.captionParagraph.innerHTML = link.title;
}
}
Test that the slide show still works, and when it does, add a switchImage method:
this.switchImage = function(link) {
this.displayImage(link);
this.lastActionTime = new Date();
return false;
}
Make sure that clicking the links still works and that the automatic slide show still works. When
they do, add a new method that will check if the page has been idle; this new method will check
that a property called lastActionTime was at least 30 seconds ago. It will return true if the page
has been “idle” and false if it has not.
this.lastActionTime = new Date();
this.idle = function() {
var now = new Date();
if (now.valueOf() > this.lastActionTime.valueOf() + 30000) {
return true;
} else {
return false;
}
}
Finally, add a new method for the advanceSlide function to call. This function will only advance
if the page has been idle.
this.idleAdvance = function() {
Image Gallery—25
if (this.idle()) {
this.advance();
}
}
1. If you want it to wait five minutes before starting the slide show, change 30000 to 300000 in
beginShow. 300,000 milliseconds is 300 seconds, which is 5 minutes.
2. If you want it to wait for one minute of inactivity before restarting the slide show, change
30000 to 60000 in the idle method of SlideShow.
3. If you want it to advance every four seconds instead of every five seconds, change 5000 to
4000 in the advanceSlide function.
Dates
There’s a new object in the idle method: the Date object. Date objects contain the current date
and time, down to the milliseconds. The method valueOf() returns the date and time of the object
as milliseconds, allowing us to easily compare two dates. In the SlideShow, we’re creating a
lastActionTime property that contains that last time the visitor manually shifted the slide show.
The idle method creates a variable to store the current time (“now”), and then compares that with
the lastActionTimeProperty—after adding 30000 milliseconds to lastActionTime.
Greater than
The “>” comparison means “is greater than”. The comparison in the idle method is, “is now in
milliseconds greater than the last action time in milliseconds with 30000 milliseconds added to
it”.
Else
If statements can have an “else” clause that specifies what to do if the comparison condition is
not met. Here, if now is not greater than the last action time plus 30 seconds, the line in the else
section applies. Thus, the idle method will either return true (the condition is met) or false (the
condition is not met).
26—Image Gallery
CURRENT VERSION
Just to make sure we’re all on the same page, here’s the current version of the SlideShow object
and related functions:
function SlideShow() {
this.slides = document.getElementById("images").getElementsByTagName('A');
this.imageFrame = document.getElementById("frame");
this.captionParagraph = document.getElementById("caption");
this.currentSlide = 0;
this.advance = function() {
this.currentSlide++;
if (this.currentSlide >= this.slides.length) {
this.currentSlide = 0;
}
this.displayImage(this.slides[this.currentSlide]);
}
this.displayImage = function(link) {
this.imageFrame.src=link.href;
this.captionParagraph.innerHTML = link.title;
}
this.switchImage = function(link) {
this.displayImage(link);
this.lastActionTime = new Date();
return false;
}
this.idleAdvance = function() {
if (this.idle()) {
this.advance();
}
}
}
//advance the slide show, and set the timer for the next advance
function advanceSlide() {
gallery.idleAdvance();
window.setTimeout(advanceSlide, 5000);
}
window.onload=beginShow;
ANIMATION
That’s how to use timers for long periods of time, but we can also set timers to extremely short
periods of time, on the order of just a few milliseconds. This is useful for animation effects.
Timers combined with style changes can make very appealing animations. For example, there is
a style called “opacity” that sets the transparency of an element. We can use this to fade out one
image and then fade in the next.
You should see the image and the caption fade to 50% opacity. Change that number to 0 and the
image and caption will fade completely. Put it back to 1 and the image and caption will return to
full strength.
Parent node
The “parentNode” of an element is the element that surrounds it, its immediate parent. The
parentNode of gallery.imageFrame is the <div> tag that encloses it. By setting that DIV
element’s opacity, the opacity of everything contained by the DIV is also affected.
FADE OUT
Create a fade function. This function will fade out until the opacity is zero, and then advance the
slide. It will return true or false depending on whether the fade is done or not.
this.fade = function() {
var done = false;
var opacity = this.imageFrame.parentNode.style.opacity;
if (opacity=="") {
28—Image Gallery
opacity = 1;
}
opacity -= .1;
if (opacity <= 0) {
opacity = 0;
done = true;
}
this.imageFrame.parentNode.style.opacity = opacity;
if (opacity == 0) {
this.advance();
this.imageFrame.parentNode.style.opacity = 1;
}
return done;
}
This function checks the opacity of the parent DIV, and reduces it by .1 each time. Note that the
first time through, the opacity doesn’t exist on the DIV’s style, so we need to check to see if it’s
empty. If it is empty, we set it to 1. When opacity drops to zero, we set the “done” variable to
true. We also compare “less than or equal to zero” instead of “equal to zero”, just in case the
numbers we’re using drop it to below zero. We set opacity to zero to ensure that it never drops
below zero.
At the end of the function, if opacity has reached zero, we advance the slide and reset opacity to
1 so that the slide is visible again.
During a fade, the advanceSlide function will use a very small number for the setTimeout
method. Once the fade is completed, it will use the normal 5,000 milliseconds.
function advanceSlide() {
if (gallery.fade()) {
window.setTimeout(advanceSlide, 5000);
} else {
window.setTimeout(advanceSlide, 10);
}
}
It knows whether or not the fade has completed by whether or not gallery.fade returns true or
false.
-=
The symbol for “-=” is very similar to the symbol “--” and “++”. Where “--” reduces the variable
by 1, “-=” reduces the variable by whatever is after the symbol. Setting “opacity -= .1” means
“reduce opacity by .1”. There’s an equivalent for increasing a variable, and that’s “+=”, which
we’ll start using when we set the new image to fade in.
Image Gallery—29
<=
The comparison “<=” means “less than or equal to”. We’re checking to see if “opacity is less
than or equal to zero”.
FADE IN
Of course, we also want the next image to fade in. To do this, we need to keep track of which
direction the fade is going. Add a new property above the fade method, “fadeDirection”, and set
it to “out”. By default, we’ll be fading out. Once we finish fading out we’ll set that property to
“in”. And once we finish fading in we’ll set it to “out” again.
if (this.fadeDirection == "out") {
opacity -= .1;
if (opacity <= 0) {
opacity = 0;
this.fadeDirection = "in";
}
} else {
opacity += .1;
if (opacity >= 1) {
opacity = 1;
done = true;
this.fadeDirection = "out";
}
}
this.imageFrame.parentNode.style.opacity = opacity;
if (opacity == 0) {
this.advance();
}
return done;
}
There’s nothing new here. We’ve added an “if” around the opacity reduction: if we’re fading out,
reduce the opacity. Otherwise, increase the opacity. When the opacity drops to zero, reverse the
fade direction. When the opacity increases to 1, remember that we’re done fading, and reverse
30—Image Gallery
And yet, when you run this, what happens? The ancient sarcophagi fades out, Carrick Castle
starts fading in… and then it stops. It’s as if the opacity isn’t changing. It’ll sit there forever. Put
a “window.alert(opacity);” just in front of “this.imageFrame.parentNode.style.opacity =
opacity;”.
You’ll be able to see the numbers drop; there are a couple of rounding errors as it drops, but they
shouldn’t matter. It starts coming up to “00.1”, and then to… “0.10.1”? And then it just keeps
repeating it. What’s that?
JavaScript is a “loosely typed” or “weakly typed” language. This means that you don’t have to
care if a particular variable contains a number or if it contains a string of text. If you try to
subtract two from five, JavaScript doesn’t care if you originally set up the variables as strings or
as numbers.
javascript:window.alert(5-2);
javascript:window.alert("5"-"2");
Some languages don’t let you do that. If you set it up as a string, you can’t do math on it. If you
set it up as a number, you can’t treat it as a string of text characters. Scripting languages are often
loosely typed; it usually makes them easier to use, especially on the web.
The problem is that JavaScript also supports “operator overloading”. Operator overloading
means that some operators (such as the “+” or, in this case, “+=”) can do different things
depending on the type of variable they’re applied to.
javascript:window.alert(5+2);
javascript:window.alert("5"+"2");
For numbers, the plus sign adds two numbers together. For strings, the plus sign (and its related
operators) concatenates both sides. If any string is involved, JavaScript converts all of the values
to a string and concatenates.
Style values are always strings. So while we can get away with “opacity -= .1”, because the
minus sign isn’t overloaded, we can’t get away with “opacity += .1”. We need to convert the
opacity to a number first. The specific kind of number we need it to be is a “float”, that is, a
number with floating decimal points.
For the “if” where we check if the opacity is empty, add an “else” to convert the opacity to a
number:
if (opacity=="") {
opacity = 1;
} else {
opacity = parseFloat(opacity);
}
If the opacity is empty, we set it to the number 1. No need to convert that to a number, because
Image Gallery—31
“1” without quotes around it is already a number. If the opacity is not empty (“else”), we use
JavaScript’s parseFloat function to convert it to a floating point number.
Now, the slides should both fade out and fade in again.
IDLE
We’ve lost the idle timer in this version, because we’re not using the idle method.
Put “if (this.idle()) {…}” around the fade code. That will cause it to not perform any fading
during the idle period of thirty seconds after the visitor clicks on one of the image links. But we
also need to think about what happens if the visitor clicks during a fade. The DIV is faded down
to .3 opacity, the visitor clicks on an image link, and then the DIV is going to be stuck at half-
fade until the slide show starts again. We need an “else” to reset all of the fade properties when
fade is called during an idle period. We’ll need to set done to true, set the opacity back to 1, and
set the fadeDirection back to “out”.
this.fadeDirection = "out";
this.fade = function() {
var done = false;
if (this.idle()) {
var opacity = this.imageFrame.parentNode.style.opacity;
if (opacity=="") {
opacity = 1;
} else {
opacity = parseFloat(opacity);
}
if (this.fadeDirection == "out") {
opacity -= .1;
if (opacity <= 0) {
opacity = 0;
this.fadeDirection = "in";
}
} else {
opacity += .1;
if (opacity >= 1) {
opacity = 1;
done = true;
this.fadeDirection = "out";
}
}
} else {
//we are not or are no longer idle
//be done and set opacity back to 1 just in case
done = true;
opacity = 1;
this.fadeDirection = "out";
}
32—Image Gallery
this.imageFrame.parentNode.style.opacity = opacity;
if (opacity == 0) {
this.advance();
}
return done;
}
The slide show will now fade until the visitor clicks, at which point it will immediately unfade.
It’ll then wait for thirty seconds before resuming the slide show.
ALWAYS ADVANCE
Currently, if the visitor revisits a slide during the slide show, the slide will pause, but it won’t
reset itself. If the last slide it displayed was Lochmaben Castle and the visitor clicks on Ancient
Sarcophagi, then after thirty seconds the slide show will resume at Lochnaw. This makes sense;
there’s no reason to revisit slides we’ve already seen just because we wanted to revisit one of the
slides.
However, there is one condition where this doesn’t make sense. Go ahead and reload the page,
and before the slide show starts click on Carrick Castle. When the slide show starts, Carrick
Castle will fade out… and then Carrick Castle will fade in again. The slide show thinks it needs
to go from slide 1 to slide 2, and doesn’t care that we’re already looking at slide 2.
As long as we can assume that there are always at least two unique images, this is an easy fix. In
the advance method, we can compare the href of the link to the src of the current image. If
they’re the same, just advance again.
this.advance = function() {
this.currentSlide++;
if (this.currentSlide >= this.slides.length) {
this.currentSlide = 0;
}
//if the new slide is the same as the current slide, advance again
if (this.slides[this.currentSlide].href == this.imageFrame.src) {
this.advance();
} else {
this.displayImage(this.slides[this.currentSlide]);
}
}
The advance method calls itself to advance again if it’s about to display the same image that’s
already onscreen. This requires that there be at least two unique images. If there’s only one
image, it will never stop. It will keep trying to advance to a unique image, and eventually the
browser will have to kill it.
Image Gallery—33
One of the nice things about using classes is that the shared information on the object makes it
easier to add new functionality. Suppose we want to be able to display a random image, as we
did earlier with a function? In the SlideShow class, it becomes a two-line function:
this.randomSlide = function() {
this.currentSlide = Math.floor(Math.random()*this.slides.length);
this.advance();
}
And then add this, for example, to the beginShow() function so that a random images shows on
page load again:
//create a gallery and start the slide show
function beginShow() {
gallery = new SlideShow();
gallery.randomSlide();
window.setTimeout(advanceSlide, 30000);
}
By manipulating the data that already exists on the object, you can easily add new functionality.
EXTRA CREDIT
If you’re following this directly from the handout, the answers (or possible answers) are revealed
directly beneath the questions. So if you want to try it yourself, don’t read too quickly.
Random integers
One of the important lessons of programming is to write code that you understand. This often
means making functions with informative names that both describe the code and make the code
easier. In the section “Random image”, Math.floor(Math.random()*maximum) is not
immediately obvious as a random integer generator. How would you write a function called
"random" that returned a random integer (a) between 0 and a parameter given to the function,
and (b) between a minimum and a maximum? This function might look like, in use, “var
randomInteger = random(4)” to provide a random integer between 0 and 3. Or it might look like
“var randomInteger = random(4, 1) to provide a random integer between 1 and 4, or “var
randomInteger = random(4, 3) to provide a random integer between 3 and 6.
Possible answer:
function random(range, floor) {
var randomInteger = Math.floor(Math.random()*range);
if (floor) {
randomInteger += floor;
34—Image Gallery
}
return randomInteger;
}
It will return a random number from 0 to the first parameter; if you give it a second parameter,
that number will be added to the initial random number. So, random(3) will produce a number
from 0 to 2, but random(3, 1) will produce a number from 1 to 3.
Possible answer: I can think of two possibilities. The first one (and the safest) is to construct a
new list from the old one, that excludes any links whose href attribute is the same as the current
img tag’s src attribute. This solution is a bit tricky, because it requires looking up the for loop in
the JavaScript documentation and it requires looking up how to add an item to an array.
//a safe random image excluding the current image
function randomImage() {
var aList = document.getElementById("images").getElementsByTagName('A');
currentImage = document.getElementById("frame");
var usableImages = new Array();
for (var aIndex=0;aIndex<aList.length;aIndex++) {
var a = aList[aIndex];
if (a.href != currentImage.src) {
usableImages.push(a);
}
}
if (usableImages) {
var aChoice = Math.floor(Math.random()*usableImages.length);
switchImage(usableImages[aChoice]);
}
}
Another possibility is to keep calling randomImage() until we get a random a tag whose href
doesn’t match the src of the current img. There’s nothing new in this solution, but it does involve
a trick that we haven’t covered yet.
//a fast random image excluding the current image
function randomImage() {
var aList = document.getElementById("images").getElementsByTagName('A');
if (aList.length > 1) {
currentImage = document.getElementById("frame");
var aChoice = Math.floor(Math.random()*aList.length);
//if the choice is the same as the current one, don't use it
Image Gallery—35
if (aList[aChoice].href == currentImage.src) {
randomImage();
} else {
switchImage(aList[aChoice]);
}
}
}
This uses recursion—randomImage is calling itself. Recursion can be a very useful tool for
programming, especially when dealing with lists of items. In this case it’s probably a bit
dangerous. We’re checking that there are multiple <a> tags before recursing, because otherwise it
would recurse forever, with each recursion choosing the only image in the list, realizing it’s the
same as the current image, and recursing. Eventually the browser will recognize that something’s
wrong and cancel the script.
Test it using “javascript:gallery.retreat()” and when it works, add it as a link on the page.
Then, add another button to turn automatic sliding on or off. Add a property called “paused”
which, when true, causes “advance” to not advance the slides. Create a method called “pause”
which sets that property to true, and another method, “play”, which sets that property to false.
For extra extra credit, use only a single link to switch between pause and play. The pause method
will change that link to read “Resume slide show” and call gallery.play(), where the play method
will change the link to read “Pause slide show” and call gallery.pause().
MENU FORM
JavaScript is often used in forms to provide feedback and guidance to the person filling out the
form. Open “menu.html” in your browser, and think about what we might do to make this form
easier to use. Note that because this is a tutorial about JavaScript and not about server-side code,
form submission is disabled. One thing you can do to test it out is replace the javascript block in
the form’s “action” attribute with “mailto:youremail”. When you submit the form, your browser
will either send it via your e-mail software, or it will send it directly via e-mail. E-mailed form
submissions aren’t often readable, however, so you may be better just leaving it as “Not
Implemented”.
When you submit the form without filling out your name, it will display an alert box explaining
that the name is required, and it will color the name input box red. If you type something into the
name field and then submit, then the browser will submit the form to the form’s “action” (in this
case, showing “Not Implemented”).
38—Menu Form
Input elements have a “value” property. This property contains the value of that element. If the
customer has typed something into the element, the value property will be what they typed.
onsubmit
One of the attributes that <form> tags can have is onsubmit. The onsubmit attribute must contain
JavaScript, and that JavaScript is performed when the form is submitted. If that JavaScript
returns true, the form is submitted. If that JavaScript returns false, the form is not submitted. The
verifyOrder function returns false if the customer field is empty, or true otherwise.
Remember, “this” in a tag is that tag. Since the “onsubmit” is in the <form> tag, “this” means
that specific form. When it gets passed as the first parameter to “verifyOrder”, the first parameter
in verifyOrder is called “form”.
ORDER REQUIRED
It doesn’t do much good to require a customer name without also requiring that the customer
choose something to order. If you look at the HTML, there are three groups of radio buttons
involved in ordering: the “drinks” group, the “sides” group, and the “dishes” group. We need to
check to see that at least one of those items is checked.
However, radio buttons are part of a group: there is usually more than one radio button with the
same name. Using form[groupname] will return a list of all of those radio buttons. You can see
this by typing “javascript:window.alert(document.forms.menu['sides'])”. It should say something
like “object collection”. We need a function that will return the value of the selected radio button.
function radioValue(form, radioGroup) {
var buttons = form[radioGroup];
for (var button=0;button<buttons.length;button++) {
if (buttons[button].checked) {
return buttons[button].value;
}
}
return false;
}
Add this to the verifyOrder function, before the line that checks customerField.value:
if (!(
radioValue(form, 'drinks') ||
radioValue(form, 'sides') ||
radioValue(form, 'dishes'))
) {
document.getElementById('orders').style.backgroundColor = "red";
window.alert("Please choose something to eat and/or drink.");
return false;
} else {
document.getElementById('orders').style.backgroundColor = "inherit";
}
Now, if you try to submit your order before choosing something from one of the menus, you’ll
be alerted, and the form will not submit. If you choose something from one of the menus, but
don’t type something in the name box, you’ll be alerted to the latter error. Only if you both
choose something from the menu and type something in the name box, will the form submit.
Radio buttons
Radio buttons almost always have values, whether they’re selected or not. With radio buttons (as
with checkboxes), the important property is checked. If the radio button is checked, it’s value
will be submitted. The checked property is either true or false.
For loops
We’ve seen while loops already, but not for loops. The “for” loop has three sections in the
parentheses, each separated by a semicolon from the others. First, there’s the initializer. In this
case, that’s “var button=0”. The for loop begins by creating a variable called button and setting it
to zero.
Second, there’s the loop test. When the loop test is met, the for loop stops and continues on to the
next line following the for loop’s closing brace. In this example, the loop test is
“button<buttons.length”. If the list of radio buttons contains four items, for example, then the
loop will end if “button” is ever four or more.
Finally, there’s the counting expression. Every time the loop comes around again, it performs the
counting expression. The counting expression usually increments and decrements the variable
created in the initializer. In this example, the counting expression is “button++”. Each time the
loop comes around again, button is incremented by one.
This means that, in this example, button will start at zero, become one, then two, then three, and
so on, until it reaches the last item in the list of buttons. For each value of button, the JavaScript
between the braces will be performed.
40—Menu Form
||
Two bars (pipes) are another form of compound conditional, like the two ampersands. Double
bars mean “or”. So this example means “if there’s a value for drinks or if there’s a value for sides
or if there’s a value for dishes”. Then, the exclamation point reverses that. So if there’s a value
for anything, the conditional is not met and no alert is displayed.
if (!(
radioValue(form, 'drinks') ||
radioValue(form, 'sides') ||
radioValue(form, 'dishes'))
) {
document.getElementById('orders').style.backgroundColor = "red";
warnings.push("Please choose something to eat and/or drink.");
} else {
document.getElementById('orders').style.backgroundColor = "inherit";
}
if (!customerField.value) {
customerField.style.backgroundColor = "red";
warnings.push("We need your name in order to deliver your order.");
} else {
customerField.style.backgroundColor = "inherit";
}
if (warnings.length) {
warnings = warnings.join("\n");
window.alert(warnings);
return false;
} else {
return true;
}
}
Now, if you leave off both an order and your name, you’ll see the two errors all at once, and both
sections of the form will be highlighted.
Menu Form—41
\n
Backslash-n is commonly used to make a new line. When you have “\n” in a string of text,
JavaScript replaces those two characters with a newline character instead. You’ll use “\n” often
when joining arrays. Backslashes are used for other special characters, too. If you need a tab, you
can use “\t”. If you really do want a backslash, use two backslashes: “\\”.
TODAY’S SPECIAL
The bottom of the menu has a list of specials. You can see that the CSS for those lines is set to
highlight the lines if the mouse moves over them. Why not, if a special is clicked, automatically
select the items in the menu that correspond to that special?
The first thing we’ll need is a function that can check the radio button in a specified group,
according to a desired value.
function markSpecial(radioGroup, special) {
var form = document.forms.menu;
var buttons = form[radioGroup];
for (var button=0;button<buttons.length;button++) {
if (buttons[button].value == special) {
buttons[button].checked = true;
return;
}
}
}
Test this:
javascript:markSpecial("sides", "eggsalad");
javascript:markSpecial("sides", "slaw");
javascript:markSpecial("dishes", "burger");
If the function is working, you should see the appropriate radio button get selected each time you
use that function in the URL bar.
A special, however, has three items. It will be easier to mark the specials if we can call one
function that, in turn, calls markSpecial for each of the three choices.
function special(drink, side, meat) {
markSpecial('drinks', drink);
markSpecial('sides', side);
42—Menu Form
markSpecial('dishes', meat);
}
This function takes three strings: the drink code, the side code, and the dish code. Try it:
javascript:special("sling", "potatosalad", "rib");
javascript:special("water", "eggsalad", "chicken");
You should now be able to click on each special to mark the menu for those orders. Go ahead and
make up some more specials. All you need to do is copy the lines that are already there, and
change the text and function call.
FAVORITE DRINK
Everybody likes it when the bartender remembers their drink from last time. Let’s make our form
remember it, too. Remembering things from past visits requires cookies. Cookies are bits of data
stored on the client’s computer. The browser sends all of the cookies relevant to your page, every
time it loads the page.
Cookies need a name, an expiration date, and a path. You’ll use the name to access the cookie’s
value later. The browser will throw out the cookie when the expiration date passes. And the
browser will use the path to choose which cookies to send your page. We’re going to send it a
path of “/” so that all pages on our site (which, for testing purposes, is most likely your local hard
drive) will get the cookie we set.
//set a cookie that will expire in 14 days
//and is valid on the entire site
function setCookie(cookieName, cookieValue) {
//get the current time
var expirationDate = new Date();
//add ten days by converting to milliseconds
expirationDate = expirationDate.valueOf() + 14*24*60*60*1000;
//turn the milliseconds back into a date
expirationDate = new Date(expirationDate);
Save this and reload the page, and you now have a way to set cookies through JavaScript.
Menu Form—43
Your browser should have a way of looking at cookies. In Safari, go into the security preferences
pane to “Show Cookies”, and search on “filecookies” to see all local cookies. In Firefox, go to
the privacy pane to show cookies, and search on the cookie’s name—in this case, “faveDrink”.
You should find the cookie and be able to see that it’s value is “Harvey Wallbanger”.
This function lets us retrieve a cookie’s value. Try it in your browser’s URL bar:
javascript:window.alert(getCookie("faveDrink"));
The next step is to remember it. The best place to do this is in verifyOrder, since that happens on
submit. Right above the line that says “return true”, add code to set the cookie if the customer
has chosen a drink:
var drink = radioValue(form, "drinks");
if (drink) {
setCookie("faveDrink", drink);
}
return true;
Try choosing some drinks, submitting the form, and then checking the value of faveDrink:
javascript:window.alert(getCookie("faveDrink"));
Now we’re pretty much done. We need a function to set the drinks radio button, and we need to
call it when the window loads.
function rememberDrink() {
var drink = getCookie('faveDrink');
if (drink) {
markSpecial('drinks', drink);
}
}
window.onload=rememberDrink;
That’s it! The menu form should now remember the last submitted drink selection whenever the
44—Menu Form
page loads.
The opposite of escape is unescape, and you can see that in getCookie we unescape the value that
we earlier escaped.
Split a string
The opposite of joining an array is splitting a string. In getCookie, we split the list of cookies,
because they’re separated by semicolons and spaces, and we split each cookie on the equal sign,
because the cookies are listed as “name=value”. Splitting on the equal sign gives us an array of
the cookie’s name as the first item and its value as the second.
JAVASCRIPT ISSUES
Many people have pop-up blocking turned on, for example, so that they don’t get advertisements
popping up on their computer; most browsers have this option built-in because the problem is so
widespread.
Some browsers let the visitor choose exactly what JavaScript functionality to allow, from the
ability to resize windows on.
When going over this tutorial in early 2007, I discovered that the very simple first example didn’t
function at all in two out of three of the then-major browsers. I’d chosen setting the text in the
status bar as the first example because it required only a single line and because its result was so
obvious. No longer; most likely the functionality was abused by phishing sites. As far as I can
tell, the ability to see changed status text is no longer available in most browsers.
Browsers will also attempt to detect whether your script is consuming too many resources on the
client computer. If the browser detects this, it will either automatically stop your script, or
present the viewer with a dire warning that the script is likely doing something wrong, and do
they want to cancel? Most likely they will.
TRUSTING JAVASCRIPT
You cannot trust JavaScript at all. Not only can the viewer turn JavaScript off, but they can also
change it. A JavaScript file is just like a web page: it gets downloaded to the client computer and
then “displayed”. The viewer has full control over the file since it is on their computer, and
malicious scripters can take advantage of this if you trust the “output” of JavaScript.
JavaScript should be used to assist the visitor, not to assist the server (or the webmaster). It
should not be used to perform authentication, nor should it be used to calculate things that are
then stored on the server. You would never want to calculate prices using JavaScript and then
charge a customer based on that calculation, for example: you’ll end up giving some people a
46—JavaScript Issues
pretty hefty discount when they realize they can manipulate the calculation themselves. This
does not mean that you cannot use JavaScript to show the viewer a quick summary of their prices
or shopping cart total. It just means that you should, once they submit their purchase, calculate
the prices on the server end as well. Remember that on the Internet, it only takes one person to
take advantage of security flaws in your web site.
JavaScript is a great feature for making your web pages easier to use. But it should not be used in
place of server-side programming. Your pages should always be accessible to people who do not
use JavaScript (it is difficult to imagine what a JavaScript would look like through a translator or
a voice reader, for example), and you should not rely on the JavaScript calculations coming to
your end intact and trustworthy.
DIFFERENT VERSIONS
Different web browsers, and different versions of web browsers, will often treat the same
JavaScript slightly differently. This will be more of an issue the more complex your JavaScript
becomes. Try to keep your JavaScript standard, try to test on a wide variety of browsers and
versions, and make sure your web pages work if your viewers end up having to turn JavaScript
off to bypass inadvertent bugs in your JavaScript. Remember that just because your code works
now, there is no guarantee that it will work with tomorrow’s browsers. Keep things simple, and
especially try not to “take advantage” of bugs in a browser’s JavaScript implementation.
Someday that bug will probably get fixed.
Separating your JavaScript into its own file also makes it much harder for your JavaScript to
interfere with your HTML in browsers that don’t implement JavaScript, or don’t implement it the
way you expect it to work.
You can put more than one <script src="…"> in your web pages, to reference more than one set
of JavaScripts. Just make sure your JavaScript functions all have unique names.
PROGRAMMING TIPS
If you can’t break it down any further, move on to the next problem. Perhaps by the time you
finish the problem you can solve, you’ll have some insight into the related problem that you
couldn’t.
TEST OFTEN
Learn to love your browser’s reload button. Whenever you make a change that might affect your
web page, test immediately. Don’t make a whole bunch of changes before testing; if your web
page no longer works, you won’t necessarily know which change caused it to fail. Write your
programs a step at a time and test often.
Test things that you don’t even expect to happen: try to think up weird scenarios and come up
with a means of testing what happens to your page under those scenarios. This will give you
valuable information about what your code is really doing.
Also, make extensive use of the “window.alert()” function to let you know the value of variables
at troublesome points.
WRITE CLEANLY
JavaScript, as a programming language, is much less forgiving than HTML or even XHTML.
Commands end with semi-colons. Upper and lower case are important. Curly brackets enclose
blocks of JavaScript code. You can’t play around with those rules and expect to get a working
script.
First, name your variables intelligently. Your variable names should describe what kind of data
you expect the variable to contain.
48—Programming Tips
You will notice that I “indent” parts of JavaScript code. Usually, anything inside of curly
brackets gets indented by one tab more than the line just above it.
This indentation isn’t necessary. If you leave it out, your scripts will work just fine—if you don’t
make any mistakes. Indenting makes it much easier to not make mistakes.
For example, if you wanted to put the date into your “Current Time:” line, we already have the
code that creates a date. Unfortunately, if you call it directly you’ll end up with “Today” as the
string, because that’s what that subroutine does. Rather than duplicating the portion of the code
that creates the actual date, move it out of the “formatDate()” subroutine. Call it something like
“makeDate()”. Then, you can call that subroutine both from “formateTime()” and from
“formatDate()”.
Another example might be the code we used for prepending a zero to numbers that are smaller
than 10. We probably should have created a subroutine that would do that for us, and then call
that subroutine every time we need to prepend zeros.
But consider the things you might want to do with a subroutine at the moment you create it.
Might we want to prepend something other than zeros? Might we want to pad to three or even
four characters instead of just to two? An appropriate subroutine might be called with “thishour =
leftpad(thishour, "0", 2);” to ensure that “thishour” has at least two characters, and prepend a
zero if it does not. One could then use “indexline = leftpad(pagenumber, ".", 4);” to prepend
periods and ensure four characters in the “indexline” string. The function might look something
like:
function leftpad(theItem, thePad, minSize) {
newItem = new String(theItem);
while (newItem.length < minSize) {
newItem = thePad + newItem;
}
Programming Tips—49
return newItem;
}
Some problems will have to be solved with server-side programming, either using server-side
includes or server-side programming such as with Perl or PHP. Trying to solve these problems
with client-side JavaScript is prone to errors, sometimes dangerous ones if money or privacy is
involved.
MORE INFORMATION
The most useful book I’ve seen for JavaScript programmers is JavaScript: The Definitive Guide,
from O’Reilly. Nowadays I tend to use the web more often, however. Some of the web pages
I’ve used while writing this tutorial are the w3schools HTML DOM pages at http://
www.w3schools.com/HTMLDOM/ and Daniel Crook’s JavaScript DOM pages at http://
krook.org/jsdom/.
Three simple tutorials guide you through the basics of JavaScript, including intercepting click
events, creating a slide show with fade effects, and creating a form that remembers what visitors
use it for.