[ Previous Section | Appendix Index | Main Index ]

Section A.3

The JavaScript Programming Language


JavaScript is a programming language that is used on Web pages. It was first developed by Netscape (the predecessor of Firefox) at about the same time that Java was introduced, and the name JavaScript was chosen to ride the tide of Java's increasing popularity. In spite of the similar names, the two languages are quite different. Actually, there is no standardized language named JavaScript. The standardized language is officially called ECMAScript, but the name is not widely used in practice, and versions of JavaScript in actual web browsers don't necessarily implement the standard exactly.

Traditionally, it has been difficult for programmers to deal with the differences among JavaScript implementations in different web browsers. We will use JavaScript to work with the HTML canvas element, a fairly recent addition to HTML. Fortunately for us, the browsers that support the canvas element use a fairly standardized version of JavaScript, and that will allow us to ignore most of the incompatibilities that have plagued web developers.

This page is a short overview of JavaScript. If you really want to learn JavaScript in detail, you might consider the book JavaScript: The Definitive Guide, by David Flanagan (although the current, sixth edition is getting a little old).


A.3.1  The Core Language

JavaScript is most closely associated with web pages, but it is a general purpose language that is used in other places too. There is a core language that has nothing to do with web pages in particular, and we begin by looking at that core.

JavaScript has a looser syntax than either Java or C. One example is the use of semicolons, which are not required at the end of a statement, unless another statement follows on the same line. Like many cases of loose syntax rules, this can lead to some unexpected bugs. If a line is a legal statement, it is considered to be a complete statement, and the next line is the start of a new statement—even if you meant the next line to be a continuation of the same statement. I have been burned by this fact with code of the form

return
       "very long string";

The "return" on the first line is a legal statement, so the value on the next line is not considered to be part of that statement. The result was a function that returned without returning a value. This also depends on the fact that JavaScript will accept any expression, such as the string on the second line, as a statement, even if evaluating the expression doesn't have any effect.

Variables in Java are not typed. That is, when you declare a variable, you don't declare what type it is, and the variable can refer to data of any type. Variables are declared using the keyword var, and they can optionally be initialized when they are declared:

var x, y;
var name = "David";

It is OK in JavaScript to declare the same variable more than once; a declaration just says that the variable exists. JavaScript also allows you to use variables without declaring them. However, doing so is not a good idea. You can prevent the use of undeclared variables, as well as certain other unsafe practices, by including the following statement at the beginning of your program:

"use strict";

Although variables don't have types, values do. A value can be a number, a string, a boolean, an object, or a function. A variable that has never been assigned a value has the special value undefined. (The fact that a function can be used as a data value might be a surprise to you; more on that later.) You can determine the type of a value, using the typeof operator: The expression typeof x returns a string that tells the type of the value of x. The string can be "number", "string", "boolean", "object", "function", or "undefined". Note that typeof returns "object" for objects of any type, including arrays. Also, typeof null is "object".

In JavaScript, "string" is considered to be a primitive data type rather than an object type. When two strings are compared using the == or != operator, the contents of the strings are compared. There is no char type. String literals can be enclosed either in double quotes or in single quotes. Strings can be concatenated with the + operator, like in Java.

There is not a strict distinction between integers and real numbers. Both are of type "number". In JavaScript, unlike Java and C, division of integers produces a real number, so that 1/2 in JavaScript is 0.5, not 0 as it would be in Java.

Although there is a boolean type, with literal values true and false, you can actually use any type of value in a boolean context. So, you will often see tests in JavaScript such as

if (x) { ... }

The value of x as a boolean is considered to be false if x is the number zero or is the empty string or is null or is undefined. Effectively, any type of value can be converted implicitly to boolean

In fact, JavaScript does many implicit conversions that you might not expect. For example, when comparing a number to string using the == operator, JavaScript will try to convert the string into a number. So, the value of 17 == "17" is true. The value of "" == 0 is also true, since in this case JavaScript converts both operands to boolean. Since this behavior is not always what you want, JavaScript has operators === and !== that are similar to == and != except that they never do type conversion on their operands. S0, for example, 17 === "17" is false.

JavaScript will also try to convert a string to a number if you multiply, divide, or subtract a string and a number—but not if you add them, since in that case it interprets the + operator as string concatenation, and it converts the number into to a string.

JavaScript does not have type-casting as it exists in Java. However, you can use Number, String, and Boolean as conversion functions. For example,

x = Number(y);

will attempt to convert y to a number. You can apply this, for example, when y is a string. If the conversion fails, the value of x will be NaN, a special value indicating "Not a Number." The Number function converts the empty string to zero.

Mathematical functions in JavaScript are defined in a Math object, which is similar to the Math class in Java. For example, there are functions Math.sin(x), Math.cos(x), Math.abs(x), and Math.sqrt(x). Math.PI is the mathematical constant π. Math.random() is a function that returns a random number in the range 0.0 to 1.0, including 0.0 but excluding 1.0.


JavaScript control structures are pretty much the same as in Java or C, including if, while, for, do..while, and switch. JavaScript has a try..catch statement for handling exceptions that is similar to Java's, but since variables are untyped, there is only one catch block, and it does not declare a type for the exception. (That is, you say, "catch (e)" rather than "catch(Exception e)".) An error can be generated using the throw statement. Any type of value can be thrown. You might, for example, throw a string that represents an error message:

throw "Sorry, that value is illegal.";

Functions in JavaScript are defined using the function keyword. Since variables are untyped, no return type is declared and parameters do not have declared types. Here is a typical function definition:

function square(x) {
    return x * x;
}

A function can return any type of value, or it can return nothing (like a void method in Java). In fact, the same function might sometimes return a value and sometimes not, although that would not be good style. JavaScript does not require the number of parameters in a function call to match the number of parameters in the definition of the function. If you provide too few parameters in the function call, then the extra parameters in the function definition get the value undefined. You can check for this in the function by testing if the typeof the parameter is "undefined". There can be a good reason for doing this: It makes it possible to have optional parameters. For example, consider

function multiple( str, count ) {
     if ( typeof count == "undefined" ) {
         count = 2;
     }
     var i;
     var copies = "";
     for (i = 0; i < count; i++) {
         copies += str;
     }
     return copies;
}

If no value is provided for count, as in the function call multiple("boo"), then count will be set to 2. Note by the way that declaring a variable in a function makes it local to the function. In fact, a variable that is declared in a function can be used anywhere in that function, even before its declaration. Functions can also access global variables.

You can also provide extra values in a function call. All the values provided are always available in the function in a special variable named arguments, which is essentially an array. For example, this makes it possible to write a sum function that takes any number of input values:

function sum() {
    var i;
    var total = 0;
    for (i = 0; i < arguments.length; i++) {
         total += arguments[i];
    }
    return total;
}

With this definition, you can call sum(2,2), sum(1,2,3,4,5), and even sum(). (The value of the last function call is zero.)

It is possible to define a function inside another function. The nested function is then local to the function in which it is nested, and can only be used inside that function. This lets you define a "helper function" inside the function that uses it, instead of adding the helper function to the global namespace.


Functions in JavaScript are "first class objects." This means that functions are treated as regular data values, and you can do the sort of things with them that you do with data: assign them to variables, store them in arrays, pass them as parameters to functions, return them from functions. In fact, it is very common to do all of these things!

When you define a function using a definition like the ones in the examples shown above, it's almost the same as assigning a function to a variable. For example, given the above definition of the function sum, you can assign sum to a variable or pass it as a parameter, and you would be assigning or passing the function. And if the value of a variable is a function, you can use the variable just as you would use the function name, to call the function. That is, if you do

var f = sum;

then you can call f(1,2,3), and it will be the same as calling sum(1,2,3). (One difference between defining a function and assigning a variable is that a function defined by a function definition can be used anywhere in the program, even before the function definition. Before it starts executing the program, the computer reads the entire program to find all the functions that it contains.)

JavaScript even has something like "function literals." That is, there is a way of writing a function data value just at the point where you need it, without giving it a name or defining it with a standard function definition. Such functions are called "anonymous functions." The syntax looks like a function definition without a name. Here, for example, an anonymous function is created and passed as the first parameter to a function named setTimeout:

setTimeout( function () {
    alert("Time's Up!");
}, 5000 );

To do the same thing without anonymous functions would require defining a standard named function that is only going to be used once:

function alertFunc() {
    alert("Time's Up!");
}

setTimeout( alertFunc, 5000 );

In C, functions can be assigned to variables and passed as parameters to functions. However, there are no anonymous functions in C. Anonymous functions are one of JavaScript's distinctive features. Soemthing similar has been added to Java 8 in the form of "lambda expressions."


A.3.2  Arrays and Objects

An array in JavaScript is an object, which includes several methods for working with the array. The elements in an array can be of any type; in fact, different elements in the same array can have different types. An array value can be created as a list of values between [ and ]. For example:

var A = [ 1, 2, 3, 4, 5 ];
var B = [ "foo", "bar" ];
var C = [];

The last line in this example creates an empty array, which initially has length zero. An array can also be created using a constructor that specifies the initial size of the array:

var D = new Array(100);  // space for 100 elements

Initially, the elements of D all have the value undefined.

The length of an array is not fixed. (This makes JavaScript arrays more similar to Java ArrayLists than they are to Java arrays.) If A is an array, its current length is A.length. The push method can be used to add a new element to the end of an array, increasing its length by one: A.push(6). The pop method removes and returns the last item: A.pop(). In fact, it is legal to assign a value to an array element that does not yet exist:

var E = [ 1, 2, 3 ];  // E has length 3
E[100] = 17;  // E now has length 101.

In this example, when a value is assigned to E[100], the length of the array is increased to make it large enough to hold the new element.

Because of their flexibility, standard JavaScript arrays are not very efficient for working with arrays of numbers. Modern web browsers define typed arrays for numerical applications. For example, an array of type Int32Array can only hold values that are 32-bit integers. Typed arrays are used extensively in WebGL; they are covered in this book when they are needed.


JavaScript has objects, but it doesn't exactly have classes, at least not in the sense that Java or C++ does. An object is essentially a collection of key/value pairs, where a key is a name, like an instance variable or method name in Java, which has an associated value. The term "instance variable" is not usually used in JavaScript; the preferred term is "property."

The value of a property of an object can be an ordinary data value or a function (which is just another type of data value in JavaScript). It is possible to create an object as a list of key/value pairs, enclosed by { and }. For example,

var pt = {  x: 17, y: 42 };

var ajaxData = {
    url: "http://some.place.org/ajax.php",
    data: 42,
    onSuccess: function () { alert("It worked!"); },
    onFailure: function (error) { alert("Sorry, it failed: " + error); }
};

With these definitions, pt is an object. It has properties pt.x, with value 17, and pt.y, with value 42. ajaxData is another object with properties including ajaxData.url and ajaxData.onSuccess. The value of ajaxData.onSuccess is a function, created here as an anonymous function. Objects are open in the sense that you can add a new property to an existing object at any time just by assigning a value. For example, given the object pt defined above, you can say

pt.z = 84;

This adds z as a new property of the object, with initial value 84.

Although JavaScript doesn't (quite) have classes, it does have constructors, which can be called with the new operator to create objects. For example,

var now = new Date();

This calls the constructor Date(), which is a standard part of JavaScript. When called with no parameters, new Date() constructs an object that represents the current date and time.

A constructor is written like an ordinary function; by convention, the name of a constructor function begins with an upper case letter. In some sense, a constructor actually defines a class. Date, for example, is often referred to as a class. It is possible to write a function that can be used as a constructor, and doing so defines something that is as close to a class as JavaScript comes. For example, let's see how to define a class to represent points in 2D:

function Point2D(x,y) {
    if ( typeof x != "undefined") {
        this.x = x;
    }
    else {
        this.x = 0;
    }
    if ( typeof y != "undefined" ) {
        this.y = y;
    }
    else {
        this.y = 0;
    }
    this.move = function(dx,dy) {
        this.x = this.x + dx;
        this.y = this.y + dy;
    }
}

When called with the new operator, this function creates an object that has properties x, y, and move. The fact that the value of move is a function makes it more like an instance method than an instance variable. Consider:

var p1 = new Point2D(17,42);
var p2 = new Point2D();

The values of p1 and p2 are objects. We would say that they are "objects of type Point2D". The value of p1.x is 17, and the value of p1.y is 42. Calling p1.move(1,2) will change the values of these properties to p1.x = 18 and p1.y = 44. (It is also possible to change these variables directly with assignment statements.) Note that p2 is created with the default value of zero for p2.x and p2.y.

The definition of the move method in this example is not done in the best way possible. The problem is that every object of type Point2D gets its own copy of move. That is, the code that defines move is duplicated for each object that is created. The solution is to use something called the "prototype" of the function Point2D.

This might take us farther into the details of JavaScript than we really need to go, but here is how it works: Every object has a prototype, which is another object. Properties of the prototype are considered to be properties of the object, unless the object is given its own property of the same name. When several objects have the same prototype, those objects share the properties of the prototype. Now, when an object is created by a constructor function, the prototype of the constructor becomes the prototype of the new object that it creates. This means that properties that are added to the prototype of a constructor function are shared by all the objects that are created by that function. This allows us to improve the definition of Point2D as follows:

function Point2D(x,y) {
    if (x) {
        this.x = x;
    }
    else {
        this.x = 0;
    }
    if (y) {
        this.y = y;
    }
    else {
        this.y = 0;
    }
}

Point2D.prototype.move = function(dx,dy) {
    this.x = this.x + dx;
    this.y = this.y + dy;
}

The properties of the prototype are shared by all objects of type Point2D. In this case, there is a single copy of move in the prototype, which is used by all such objects.

The definition of Point2D uses the special variable this to refer to the object that is being constructed, so that this.x and this.y refer to properties of that object. In the definition of move, this refers to the object that is used to call the function. That is, when executing the statement pt.move(a,b), this refers to pt. (This is subtly different from the meaning of this in Java.) The use of this is mandatory in JavaScript; it is never possible to abbreviate "this.x" as just "x".


It is possible in JavaScript to make things that act like subclasses, and you might need to do that to implement complex data structures such as scene graphs. I will very briefly discuss one way to do it: The prototype object for the subclass should be an object belonging to the superclass. The prototype has a property named constructor; the value of the property for the subclass prototype should be set equal to the subclass constructor function. The subclass constructor needs to call the superclass constructor. This can be done using the call() method of the superclass constructor function. The parameters to call() should be this followed by any parameters that are required by the superclass constructor. You will probably have to do some research to understand all this, but here is an outline of what the code will look like:

function MainClass(a,b) {  // Constructor for the superclass.
    ... // initialize properties common to this class and all subclasses
}
...  // Add some methods to MainClass.prototype

function SubClass(a,b,c,d) {  // Constructor for the subclass.
    MainClass.call(this,a,b); // Call the superclass constructor.
    ... // initialize extra properties needed for this subclass
}
SubClass.prototype = new MainClass();  // Use a MainClass object as prototype.
SubClass.prototype.constructor = SubClass;  // Change its constructor property.
...  // Add some methods to SubClass.prototype

A.3.3  JavaScript on Web Pages

There are three ways to include JavaScript code on web pages (that is, in HTML files). First, you can include it inside <script> elements, which have the form

<script>
    
    // ... JavaScript code goes here ...
    
</script>

You will sometimes see a type attribute in the first line, as in

<script type="text/javascript">

The attribute specifies the programming language used for the script. However, in HTML5, the value "text/javascript" is the default and the type attribute is not required for JavaScript scripts.

Second, you can put JavaScript code in a separate file, usually with a name ending with ".js", and import that file into the web page. A JavaScript file can be imported using a variation of the <script> tag that has the form

<script src="filename.js"></script>

where "filename.js" should be replaced by the URL, relative or absolute, of the JavaScript file. The closing tag, </script>, is required here to mark the end of the script, even though it is not permitted to have any code inside the script element. (If you do, it will be ignored.) Importing JavaScript code from a file in this way has the same effect as typing the code from the file directly into the web page.

Script elements of either type are often included in the <head> section of an HTML file, but they actually occur at any point in the file. You can use any number of script elements on the same page. A script can include statements such as function calls and assignment statements, as well as variable and function declarations.

The third way to use JavaScript on a web page is in event handlers that can occur inside HTML elements. For example, consider

<h1 onclick="doClick()">My Web Page</h1>

Here, the onclick attribute defines an event handler that will be executed when the user clicks on the text of the <h1> element. The value of an event handler attribute such as onclick can be any JavaScript code. It can include multiple statements, separated by semicolons, and can even extend over several lines. Here, the code is "doClick()", so that clicking the <h1> element will cause the JavaScript function doClick() to be called. I should note that this is a very old-fashioned way to attach an event handler to an element, and it should not be considered good style. There are better alternatives that I will mention later. Nevertheless, I sometimes do things the old-fashioned way.

It is important to understand that all the JavaScript code in <script> elements, including code in imported files, is read and executed as the page is being loaded. Usually, most of the code in such scripts consists of variable initializations and the definitions of functions that are meant to be called after the page has loaded, in response to events. Furthermore, all the scripts on a page are part of the same program. For example, you can define a variable or function in one script, even in an imported file, and then use it in another script.


JavaScript for web pages has several standard functions that allow you to interact with the user using dialog boxes. The simplest of these is alert(message), which will display message to the user in a popup dialog box, with an "OK" button that the user can click to dismiss the message.

The function prompt(question) will display question in a dialog box, along with an input field where the user can enter a response. The prompt function returns the user's response as its return value. This type of dialog box comes with an "OK" button and with a "Cancel" button. If the user hits "Cancel", the return value from prompt is null. If the user hits "OK", the return value is the content of the input field, which might be the empty string.

The function confirm(question) displays question in a dialog box along with "OK" and "Cancel" buttons. The return value is true or false, depending on whether the user hits "OK" or "Cancel".

Here, for example, is a simple guessing game that uses these functions for user interaction:

alert("I will pick a number between 1 and 100.\n"
         + "Try to guess it!");
         
do {

    var number = Math.floor( 1 + 100*Math.random() );
    var guesses = 1;
    var guess = Number( prompt("What's your guess?") );
    while (guess != number ) {
        if ( isNaN(guess) || guess < 1 || guess > 100 ) { 
            guess = Number( prompt("Please enter an integer\n" +
                              "in the range 1 to 100") );
        }
        else if (guess < number) {
            guess = Number( prompt("Too low.  Try again!") );
            guesses++;
        }
        else {
            guess = Number( prompt("Too high.  Try again!") );
            guesses++;
        }
    }
    alert("You got it in " + guesses + " guesses.");
    
} while ( confirm("Play again?") );

(This program uses Number() to convert the user's response to a number. If the response cannot be parsed as a number, then the value will be the not-a-number value NaN. The function isNaN(guess) is used to check whether the value of guess is this special not-a-number value. It's not possible to do that by saying "if (guess == NaN)" since the expression NaN == NaN evaluates to false! The same, by the way, is true of the not-a-number value in Java.)


You can try out JavaScript code in the JavaScript consoles that are available in many web browsers. In the Chrome browser, for example, you can access a console in the menu under "More Tools" / "Developer Tools", then click the "Console" tab in the developer tools. This will show the web console at the bottom of the Chrome window, with a JavaScript input prompt. The console can also be detached into a separate window. When you type a line of JavaScript and press return, it is executed, and its value is output in the console. The code is evaluated in the context of the current web page, so you can even enter commands that affect that page. The Web console also shows JavaScript errors that occur when code on the current web page is executed, and JavaScript code can write a message to the console by calling console.log(message). All this makes the console very useful for debugging.

Other browsers have similar developer tools. For the JavaScript console in Firefox, look for "Browser Console" under "Developer" or "Web Developer" in the menu. In the Safari browser, use "Show Error Console" in the "Develop" menu (but note that the Develop menu has to be enabled in the Safari Preferences, under the "Advanced" tab). In Internet Explorer or Edge browser, access "Developer Tools" by hitting the F12 key.

When an error occurs on a web page, you don't get any notification, except for some output in the console. So, if your script doesn't seem to be working, the first thing you should do is open the console and look for an error message. When you are doing JavaScript development, you might want to keep the console always open.


A.3.4  Interacting with the Page

JavaScript code on a web page can manipulate the content and the style of that page. It can do this because of the DOM (Document Object Model). When a web page is loaded, everything on the page is encoded into a data structure, defined by the DOM, which can be accessed from JavaScript as a collection of objects. There are several ways to get references to these objects, but I will discuss only one: document.getElementById. Any element on a web page can have an id attribute. For example:

<img src="somepicture.jpg" id="pic">

or

<h1 id="mainhead">My Page</h1>

An id should be unique on the page, so that an element is uniquely identified by its id. Any element is represented by a DOM object. If an element has an id, you can obtain a reference to the corresponding DOM object by passing the id to the function document.getElementById. For example:

var image = document.getElementById("pic");
var heading = document.getElementById("mainhead");

Once you have a DOM object, you can use it to manipulate the element that it represents. For example, the content of the element is given by the innerHTML property of the object. The value is a string containing text or HTML code. In our example, the value of heading.innerHTML is the string "My Page". Furthermore, you can assign a value to this property, and doing so will change the content of the element. For example:

heading.innerHTML = "Best Page Ever!";

This does not just change the value of the property in the object; it actually changes the text that is displayed on the web page! This will seem odd (and maybe even a little creepy) to programmers who are new to JavaScript: It's an assignment statement that has a side effect. But that's the way the DOM works. A change to the DOM data structure that represents a web page will actually modify the page and change its display in the web browser.

Some attributes of elements become properties of the objects that represent them. This is true for the src attribute of an image element, so that in our example, we could say

image.src = "anotherpicture.jpg";

This will change the source of the image element. Again, this is a "live" assignment: When the assignment statement is executed, the image on the web page changes.

For readers who know CSS, note that the DOM object for an element has a property named style that is itself an object, representing the CSS style of the object. The style object has properties such as color, backgroundColor, and fontSize representing CSS properties. By assigning values to these properties, you can change the appearance of the element on the page. For example,

heading.color = "red";
heading.fontSize = "150%";

These commands will make the text in the <h1> element red and 50% larger than usual. The value of a style property must be a string that would be a legal value for the corresponding CSS style.

Most interesting along these lines, perhaps, are properties of input elements, since they make it possible to program interaction with the user. Suppose that in the HTML source of a web page, we have

<input type="text" id="textin">

<select id="sel">
   <option value="1">Option 1</option>
   <option value="2">Option 2</option>
   <option value="3">Option 3</option>
</select>

<input type="checkbox" id="cbox">

and in JavaScript, we have

var textin = document.getElementById("textin");
var sel = document.getElmenntById("sel");
var checkbox = document.getElementById("cbox");

Then the value of the property checkbox.checked is a boolean that can be tested to determine whether the checkbox is checked or not, and the value true or false can be assigned to checkbox.checked to check or uncheck the box programmatically. The value of checkbox.disabled is a boolean that tells whether the checkbox is disabled. (The user can't change the value of a disabled checkbox.) Again, you can both test and set this value. The properties sel.disabled and textin.disabled do the same thing for the <select> menu and the text input box. The properties textin.value and sel.value represent the current values of those elements. The value of a text input is the text that is currently in the box. The value of a <select> element is the value of the currently selected option. As an example, here is complete source code for a web page that implements a guessing game using a text input box and buttons:

<!DOCTYPE html>
<html>
<head>
<title>Guessing Game</title>
<script>
    "use strict";
    var number = Math.floor( 1 + 100*Math.random() );
    var guessCount = 0;
    var guessMessage = "Your guesses so far: ";
    function guess() {
        var userNumber = Number( document.getElementById("guess").value );
        document.getElementById("guess").value = "";
        if ( isNaN(userNumber) || userNumber < 1 || userNumber > 100 ) {
            document.getElementById("question").innerHTML =
               "Bad input!<br>Try again with an integer in the range 1 to 100.";
        }
        else if (userNumber == number) {
            guessCount++;
            document.getElementById("question").innerHTML =
                "You got it in " + guessCount + " guesses. " +
                userNumber + " is correct.<br>" + 
                "I have picked another number.  Make a guess!";
            number = Math.floor( 1 + 100*Math.random() );
            guessCount = 0;
            guessMessage = "Your guesses so far: ";
            document.getElementById("message").innerHTML = "";
        }
        else if (userNumber < number) {
            guessCount++;
            document.getElementById("question").innerHTML =
                userNumber + " is too low.<br>Try again.";
            guessMessage += " " + userNumber;
            document.getElementById("message").innerHTML = guessMessage;
        }
        else {
            guessCount++;
            document.getElementById("question").innerHTML =
                userNumber + " is too high.<br>Try again.";
            guessMessage += " " + userNumber;
            document.getElementById("message").innerHTML = guessMessage;
        }
    }
</script>
</head>
<body>
    <p id="question">I will pick a number between 1 and 100.<br>
     Try to guess it.  What is your first guess?</p>
    <p><input type="text" id="guess">
       <button onclick="guess()">Make Guess</button></p>
    <p id="message"></p>
</body>
</html>

Here's one problem with some of my discussion. Suppose that a script uses document.getElementById to get the DOM object for some HTML element. If that script is executed before the page has finished loading, the element that it is trying to access might not yet exist. And remember that scripts are executed as the page is loading. Of course, one solution is to call document.getElementById only in functions that are executed in response to events that can only occur after the page has loaded; that's what I did in the previous example. But sometimes, you want to assign a DOM object to a global variable. Where should you do that? One possibility is to put the script at the end of the page. That will probably work. Another, more common technique is to put the assignment into a function and arrange for that function to run after the page has finished loading. When the browser has finished loading the page and building its DOM representation, it fires an onload event. You can arrange for some JavaScript code to be called in response to that event. A common way of doing this is to add an onload event-handler to the <body> tag:

<body onload="init()">

This will call a function named init() when the page has loaded. That function should include any initialization code that your program needs.

You can define similar event-handlers in other elements. For example, for <input> and <select> elements, you can supply an onchange event-handler that will be executed when the user changes the value associated with the element. This allows you to respond when the user checks or unchecks a checkbox or selects a new option from a select menu.

It's possible to include an event handler for an element in the HTML tag that creates the element, as I did with the body onload event. But that's not the preferred way to set up event handling. For one thing, the mixing of JavaScript code and HTML code is often considered to be bad style. Alternatively, there are two other ways to install event handlers using the DOM. Suppose that checkbox is a DOM object representing a check box element, probably obtained by calling document.getElementById. That object has a property named onchange that represents an event-handler for the checkbox's onchange event. You can set up event handling by assigning a function to that property. If checkBoxChanged is the function that you want to call when the user checks or unchecks the box, you can use the JavaScript command:

checkbox.onchange = checkBoxChanged;

You could also use an anonymous function:

checkbox.onchange = function() { alert("Checkbox changed"); };

Note that the value of checkbox.onchange is a function, not a string of JavaScript code.

The other way to set up event handling in JavaScript is with the addEventListener function (at least in modern browsers, including all of those that support the HTML canvas element). This technique is more flexible because it allows you to set up more than one event handler for the same event. This function is a method in any DOM element object. Using it, our checkbox example becomes

checkbox.addEventListener( "change", checkBoxChanged, false );

The first parameter to addEventListener is a string that gives the name of the event. The name is the same as the name of the event attribute in HTML, with "on" stripped off the front: onchange becomes "change". The second parameter is the function that will be called when the event occurs. It can be given as the name of a function or as an anonymous function. The third parameter is harder to explain and will, for our purposes, always be false. You can remove an event listener from an element by calling element.removeEventListener with the same parameters that were used in the call to element.addEventListener.

Similarly, there is an onmousedown event that is defined for any element. A handler for this event can be attached to a DOM element, elem, either by assigning a function to elem.onmousedown or by calling elem.addEventListener("mousedown",handler,false). Other common events include onmouseup, onmousemove, onclick, and onkeydown. An onkeydown event handler responds when the user presses a key on the keyboard. The handler is often attached to the document object:

document.onkeydown = doKeyPressed;

An event-handler function can take a parameter that contains information about the event. For example, in an event-handler for mouse events, using evt as the name of the parameter, evt.clientX and evt.clientY give the location of the mouse in the browser window. In a handler for the onkeydown event, evt.keyCode is a numeric code for the key that was pressed.

Event handling is a complicated subject, and I have given only a short introduction here. As a first step in learning more about events in JavaScript, you might look at the sample program canvas2d/EventsStarter.html.


[ Previous Section | Appendix Index | Main Index ]