You are on page 1of 27

LI-JavaScript (19/3/07) © P.

Coxhead, 2007 Page 1

Introduction to JavaScript for Java Programmers


Peter Coxhead

Contents
0 JavaScript covered in this handout
1 Introduction
2 JavaScript and Java
3 Data Types in JavaScript
4 Numbers
5 Strings
6 Booleans
7 Objects (including document, Date)
8 Arrays
9 Functions
10 Equality Tests
11 Scope and Existence
12 Events and Event Handling
13 Creating Objects
14 Manipulating CSS
15 Manipulating the HTML DOM
16 Loading and processing XML

0 JavaScript covered in this handout


This handout is concerned with JavaScript as used in HTML pages for display in a web
browser – so-called ‘client-side JavaScript’. Be aware that there are other uses of JavaScript
for which statements written here may not be true; throughout by ‘JavaScript’ I mean only
‘web browser JavaScript’.
For programmers with a knowledge of Java, learning the core JavaScript language is reason-
ably straightforward. Learning to use JavaScript effectively is an entirely different matter.
The language itself is evolving; browsers have their own idiosyncratic implementations;
libraries and other sources of code are constantly being updated. The solution is to use the
web. For example, the easiest way to check the syntax of the switch statement in JavaScript
is to put “javascript syntax switch” into a search engine. To deal with browser differences,
try “javascript browser compatibility”. There are a number of good tutorial websites on
JavaScript, among them http://www.howtocreate.co.uk/tutorials/javascript/, by Mark Wilton-
Jones.
The JavaScript described here is designed to work with Firefox, Version 2 onwards, and
other so-called ‘fifth generation browsers’.
By its nature, client-side JavaScript is freely open to inspection and copying. Don’t re-invent
the wheel, but do acknowledge any code you re-use and observe any stated restrictions. Note
however that JavaScript on the web is not always written in a style which should be
emulated.

1 Introduction
The JavaScript language dates back to 1996 and the release of Version 2 of the Netscape
browser. It offered two key features:
• When included in HTML pages, it was executed by the Netscape browser, thus allowing
web pages to have dynamic features.
• A page displayed by the Netscape browser was parsed in such a way that an internal
‘document object model’ (DOM) was created which was accessible to the JavaScript
language since it mapped HTML elements to JavaScript objects.
Page 2 LI-JavaScript (19/3/07)

Netscape did not intend JavaScript to be a proprietary standard, and so passed the language to
the European Computer Manufacturers Association (ECMA) for standardisation. This result-
ed in a language called ECMAscript, although the name never caught on. The ECMA stan-
dard defined the core syntax of JavaScript, but did not fully define the JavaScript DOM.
JavaScript then got caught up in the ‘browser wars’, when Microsoft decided that a web
browser was too important a piece of software to be left to independent companies and,
whether intentionally or not, set about driving Netscape out of business. The result has been a
very complex and tangled history whereby different browsers implement:
• different versions of JavaScript (e.g. Microsoft’s JSoft version with its own extensions)
• different JavaScript DOMs.
The variations in JavaScript are smaller and hence usually less important than the variations
in DOMs. The latest browsers show some degree of convergence, but so long as users
employ older versions, JavaScript authors face serious problems in creating compatible web
pages. The issue will largely be avoided in this introductory module. The best way to find
information on this rapidly changing subject is to use a search engine: a good starting point is
“javascript browser compatibility”.
Returning to the two key features introduced by Netscape, it’s important to be clear from the
start that both are essential to the successful use of JavaScript.
For example, suppose we want to display an image in a web page, and then change the image
when the user clicks on it. Here’s a simple XHTML page which does this.
Example 1-1 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex1-1.html)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1"/>
<title>Example 1-1</title>
<script type="text/javascript">
<!--
var pic=new Array()
pic[0]=new Image(); pic[0].src='image1.jpg'
pic[1]=new Image(); pic[1].src='image2.jpg'
var i=0 // current image
function step(imgNo)
{ i++
if(i>1) i=0
window.document.images[imgNo].src = pic[i].src
}
-->
</script>
</head>
<body>
<p>Click on the image to change it.</p>
<p><img src="image1.jpg" alt="a clickable image"
height="198" width="144" onclick="step(0)"/>
</p>
</body>
</html>
Two things to note first about this example are:
• It is essential that it is served by the web server as "text/html" and not e.g. as "text/xml".
The JavaScript in the document makes it not well-formed as XML;1 further the browser

1
For a more detailed explanation, see the section on validity in my Summary of Core XHTML.
LI-JavaScript (19/3/07) Page 3

will only attempt to execute JavaScript when given an HTML document. The server may
or may not use the meta element to determine how the document should be served.
• JavaScript treats <!-- as the start of a one-line comment (as well as the more properly
used //). This allows JavaScript to be embedded in what looks like a comment to older
browsers; hence the two ‘hiding’ lines above. Since I’m not dealing with browser
compatibility here, I will leave out these lines in future examples; strictly they should
not be used in XHTML anyway.
The JavaScript in the example consists of the block of code:
var pic=new Array()
pic[0]=new Image(); pic[0].src='image1.jpg'
pic[1]=new Image(); pic[1].src='image2.jpg'
var i=0 // current image
function step(imgNo)
{ i++
if(i>1) i=0
document.images[imgNo].src = pic[i].src
}
plus the value step(0) of the onclick attribute of the img element.1
The block of code could equally well be stored in a separate file, say ex1-1.js. The entire
script element would then be replaced by
<script type="text/javascript" src="ex1-1.js"/>
Code loaded in this way is treated exactly as if written in the same place.
JavaScript can be placed either in the head or the body of the page. In general, it’s better to
put as little JavaScript as possible in the body of an HTML page. A useful rule of thumb is to
have only a single JavaScript statement in script elements in the body; more than this should
be moved to the head and made it a function to be called from the body. It’s much harder to
understand (and hence debug) both the HTML and the JavaScript when they are mixed up.
Wherever it’s placed, the browser processes JavaScript sequentially as the page is loaded.
JavaScript statements which are ‘free’ (i.e. not inside a function declaration) are executed as
they are processed. So before the body element is processed, an array pic will have been set
up with two elements of type Image (a predefined object type), each with its src field set to
the name of a JPG image file. Setting src causes the browser to load the image. The variable
i (representing the index of the current image) will have been set to 0.
Functions, indicated by the function keyword, are parsed and stored, but not executed. This
enables functions to refer to things which haven’t yet been created, whereas executed code
cannot. For example, immediately after
var i=0 // current image
it would be syntactically correct to write
window.document.images[0].src = pic[i].src
However, the browser would attempt to execute this statement, which would result in an
error, since although i has the value 0 and pic[i] exists, window .document.images[0]
does not yet exist – it is only created by the browser when it reaches the first img element in
the body of the HTML.
After the page has been displayed, the DOM becomes relevant. The browser must know that
the img element has an onclick attribute, whose value is to be executed when the user clicks
on the image. Execution here calls the function step with imgNo given the value 0 (meaning
the first image in the document).

1
HTML and JavaScript allow matched single and double-quotes to be used interchangeably. I generally use
double-quotes for attribute values and single quotes for JavaScript strings as here. Use of either this or the
opposite convention avoids un-necessary escaping.
Page 4 LI-JavaScript (19/3/07)

The DOM is needed again to ensure that the expression window.document.images[imgNo]


inside the function step1 refers to the JavaScript object corresponding to the first img
element in the web page. The browser window is represented as the window object. Its
document field represents the content of the browser window; this in turn has a field images
which is an array of all the images (img elements) in the HTML. Each image is represented
by an object of type Image; such objects have a field src which gives the URL of the image
file.
A better style than using the images array is to use the getElementById method of the
document object. Given a string as its argument, this returns the element of the document
whose id is equal to that string. This avoids relying on a detailed knowledge of the order of
images in the document and also of the DOM hierarchy.2
It’s unusual to click on an image to change it; a further improvement is to use a button, since
this is a more obvious way of achieving an action. Both revisions are shown below.
Example 1-2 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex1-2.html)
. . . . . . . . . .
function step(imgID)
{ i++;
if(i>1) i=0
document.getElementById(imgID).src = pic[i].src
}
. . . . . . . . . .
<body>
<p><img id="i01" src="image1.jpg" alt="a changeable image"
height="198" width="144" /> <br/>
<input type="button" value="Next" onclick="step('i01')" />
</p>
</body>

2 JavaScript and Java


I don’t intend to present a complete overview of even the latest version of JavaScript, let
alone earlier versions. As always the web is the best source of up-to-date information.
As its name suggests,3 JavaScript has a similar syntax to Java. For programmers familiar
with Java, this is both an advantage and a disadvantage.
• It’s an advantage because it’s not necessary to learn the detailed syntax of constructs
such as if statements, loops, etc. Much JavaScript code is exactly the same as it would
be in Java.
• It’s a disadvantage because JavaScript is a fundamentally different language to Java.
Some differences between Java and JavaScript syntax:
• JavaScript allows statements to be terminated by a newline, so does not require semi-
colons after every statement. (On the other hand, it is generally considered good
programming practice to include the semicolons and I will do so in future examples.)
Care is needed when splitting statements between lines, since if each of the lines is
separately valid, JavaScript will treat them as separate statements.
• Although JavaScript does have objects and the equivalent of classes, their syntax is
significantly different. In particular JavaScript does not require an ‘outer’ class, as we
have seen in the example above, nor any special main method. Many JavaScript pro-

1
When talking about languages such as Java or C, it is conventional to put empty parentheses after the name
of a method or function to distinguish it from the name of a variable or field. However, in JavaScript, as
will be explained later, function values can be assigned to variables, so that it is important to distinguish the
name of function from a use or call of the function. Hence in this document the name of a function will
always be written without parentheses.
2
This approach is particularly likely to fail with older browsers.
3
Until its release, it was apparently called LiveWire or LiveScript.
LI-JavaScript (19/3/07) Page 5

grams contain no overt classes at all, simply consisting of function definitions and
statements (including function calls) embedded in an HTML page.
• The keyword function is needed when defining functions (the equivalent of methods in
Java).
• Because JavaScript is not a strongly typed language,1 declarations of variables and func-
tion arguments do not include type information.
• The rules defining the scope of variables are different. JavaScript does not have ‘block
level scope’. Java programmers need to take care not to inadvertently over-write existing
variables when writing JavaScript.
• The set of ‘reserved words’ which cannot be used as identifiers is somewhat different:
see e.g. http://www.javascriptkit.com/jsref/reserved.shtml. Further, since global vari-
ables are actually fields of the window object, care must be taken not to over-ride
existing fields. For maximum compatibility, values of id attributes in the HTML should
not be used as identifiers.

3 Data Types in JavaScript


A major difference between JavaScript and Java is that JavaScript variables are not of fixed
type. In particular:
• Variables are not confined to one type. Variables should normally be declared using the
keyword var. Either when declared or later, they can be assigned values of any valid
type. Thus the following is legal in JavaScript:
var count = 0; // declared and initialized to a number value
count = count + 1;
count = 'Error: too many values entered.'; // given a string value
• During execution, JavaScript values are automatically converted from one type to an-
other to ensure type compatibility. Usually this has the expected effect, but care is
needed. Consider this code:
var product = 2 * '10' + 3;
var sum = 2 + '10' + 3;
var msg = 'Distance is '+(2+3)+' km';
This results in product having the numerical value 23, since the string '10' is convert-
ed to a number to be compatible with the * operator. However sum has the string value
'2103', since, as in Java, the + operator is overloaded and is assumed to refer to string
concatenation if either of its arguments is a string. The parentheses are essential if the
value of msg is to be ‘Distance is 5 km’; if they are omitted the value will be ‘Distance is
23 km’.
JavaScript has three non-composite data types: numbers, strings and booleans, and one com-
posite type: object. Arrays are a special kind of object, as are functions.
As in Java, the distinction is important when considering the effect functions can have on the
values of their arguments:
• If a variable has a non-composite data value, then if it is used as the argument of a func-
tion, its value outside the function cannot be changed.
• If a variable has a composite data value, then changing a component of that value inside
a function changes the value outside the function.
Consider the following schematic code (where material in angle brackets isn’t JavaScript).

1
JavaScript has untyped variables in that their type is not fixed. However JavaScript values are typed; the
typeof operator can always be applied to determine the current type of a variable and types and type con-
versions are important in understanding how JavaScript works. It’s thus slightly misleading when Java-
Script is said to be an untyped language.
Page 6 LI-JavaScript (19/3/07)

function test(a,b)
{ // Point 1
a = <new value of any type>;
// Point 2
b<component identifier> = <new value of any type>;
// Point 3
}
var x = <non-composite value>;
var y = <composite value>;
test(x,y);
At ‘Point 1’, both the global variable x and the parameter a refer to the same value. At ‘Point
2’, a is changed to refer to a new value, but this does not change the value to which x refers.
At ‘Point 2’, both the global variable y and the parameter b refer to the same composite
value. However, between ‘Point 2’ and ‘Point 3’, the content of what both y and b refer to is
changed. At ‘Point 3’, y and b still refer to the same value but that value is now different as it
has a changed component.

4 Numbers
JavaScript does not distinguish between integers and reals. All numbers are represented in 8
byte IEEE floating point numeric format, regardless of how they are written in the program.
It is important not to rely on exact integer arithmetic when writing JavaScript. For example,
it is dangerous to test computed numerical values for equality, even if it appears that only
integer operations have been carried out.
IEEE floating point numbers include values displayed in JavaScript as Infinity, -Infinity
and NaN (‘Not a Number’).
var result = 2 / 0; // result has the value Infinity
result = -2 / 0; // result has the value -Infinity
result = 0 / 0; // result has the value NaN
JavaScript has many predefined objects and classes, among them Number and Math.
Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY and Number.NaN provide an-
other way of creating the three special numerical values. Note that although equality tests
work for Infinity and -Infinity, they do not for NaN. The built-in function isNaN must be
used.
2 / 0 == Number.POSITIVE_INFINITY // has the value true
-2 / 0 == Number.NEGATIVE_INFINITY // has the value true
0 / 0 == Number.NaN // has the value false
isNaN(0 / 0) // has the value true
The Math ‘class’ gives access to a wide range of mathematical constants and functions. For
example, Math.sqrt(x) calculates the square root of x.

5 Strings
Strings in JavaScript are similar to those in Java, with some exceptions:
• JavaScript does not have a character type, so there is no distinction between 'a' and
"a" ; both are ways of writing the same string. The word don’t can be represented in
JavaScript as "don't" or as 'don\'t'. A useful convention is to use double-quotes for
HTML attribute values and single-quotes for JavaScript strings.
• The == operator can be used with strings. Given the code
var s1 = 'Hello!';
var s2 = 'Hello!';
var s3 = s1;
all of the following expressions are true:
LI-JavaScript (19/3/07) Page 7

s1 == s2
s1 == s3
s3 == 'Hello!'
More surprisingly perhaps the expression 3 == '3' is also true because JavaScript
applies type conversion to the operands of the == operator; equality and identity
operators are discussed further in Section 10 below.
• All the comparison operators work with strings; type conversion will be applied so that
the expression 'Aa' > 'AA' && '30' > 30-1 is true.

6 Booleans
JavaScript has the boolean values true and false.
Basic and C programmers will not be surprised to learn that the expression 10 * true - 3
* false has the value 10, since true and false are converted to 1 and 0 in this context.
Never take advantage of this in writing code!
In type conversions, 0, '', null and undefined are all treated as false, all other values as
true. Again, never take advantage of this!

7 Objects (including document, Date)


JavaScript is an object-oriented language but handles objects very differently from Java.
Strictly speaking JavaScript does not have classes and class hierarchies in the Java sense; it
has ‘prototype-based inheritance’ rather than ‘class-based inheritance’. However, it’s
convenient to use the word ‘class’ when discussing JavaScript constructs that parallel those
in Java.
Many of the objects used in JavaScript are created by the browser. In the earlier example, we
saw that the window object was the top level JavaScript object, representing the browser
window (assuming there are no frames). Where there would otherwise be no ambiguity, a
reference to the top level w i n d o w object can be omitted. Thus instead of the full
window.document we can just write document.
The document object represents the HTML document as displayed in the browser window.
Among many other fields, it has a field images which is a variable length array of images
(each of ‘type’ Image).

Installing Firebug in Firefox makes it easy to examine the fields and methods of predefined
objects. (Firebug is available from http://getfirebug.com/.) For example:
• Open http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex1-1.html in Firefox.
• Open Firebug (Tools menu).
• Set the tab in the left-hand panel to show ‘HTML’ and the tab in the right-hand panel to
‘DOM’, choosing ‘Show DOM Properties’ from the ‘Options’ menu which appears to
the extreme right. The left-hand panel will now show the structure of the HTML proper;
selecting an item in this panel will show the structure of the corresponding JavaScript
object. For example, with the img element selected in the left-hand panel, the right-hand
panel shows the structure of the corresponding Image object (as generated by Firefox –
other browsers may differ!).
• Alternatively, select the ‘DOM’ tab to the left and a single panel will be shown. This
starts with the top-level window object which can then be explored.
‘Console logging’ in Firebug is also extremely useful in developing and debugging
JavaScript. See http://getfirebug.com/logging.html.
Page 8 LI-JavaScript (19/3/07)

JavaScript offers two methods of accessing the fields of objects.


• The standard Java-style syntax can be used. Thus document.images accesses the
images field of the document object.
• Objects can be treated as if they were arrays, with a string representing the name of the
field used as the index. In JavaScript a.b and a['b'] are exactly equivalent. Thus
document.images[1] is the same as document['images'][1]. The advantage of this
notation is that an arbitrary field can be selected, since the ‘array index’ can be a
variable. Thus a[x] selects the field of object a whose name is the string value of x
(assuming that such a field exists).
A typical application of JavaScript is to change the fields of browser-created DOM objects.
For example, if the function step in Example 1-2 above is changed to the following:
function step(imgID)
{ i++;
if(i>1) i=0;
var theImg = document.getElementById(imgID);
theImg.width += 10;
theImg.src = pic[i].src;
}
then each time the button is clicked, not only does the image change, but it also becomes
wider and more distorted. (See
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex1-3.html.)
Objects can have methods (i.e. functions). An important method of the document object is
write. When executed this inserts its output into the HTML source at the point at which it
appears.1 This will only cause a change in the page displayed if the generated HTML is then
processed by the browser.
Suppose if we change the body in Example 1-2 above to:
<body>
<p><img id="i01" src="image1.jpg" alt="a changeable image"
height="198" width="144" /> <br/>
<button onclick="step('i01');">Next</button>
</p>
<p>
<script type="text/javascript">
document.write('Currently showing image '+i);
</script>
</p>
</body>
We might hope that this would display a message saying which image is currently being
shown. If so, we will be disappointed. The text ‘Currently showing image 0’ will be inserted
into the HTML once and once only, namely the first time that the JavaScript is executed.
(Re-loading the page doesn’t change this, because it will cause the JavaScript in the header to
be re-executed, setting i back to 0.) Section 12 below shows one way of achieving dynamic
messages.
JavaScript has the built-in ability to create ‘date’ objects, which make handling dates and
times easier. When initially constructed, a new Date object will hold the current date and
time:
var now = new Date(); // Holds current date and time
var dayInMonth = now.getDate(); // Day of the month, from 1 to 31
var dayInWeek = now.getDay(); // Day of the week, from 0 (Sun) to 6 (Sat)
var month = now.getMonth(); // Month, from 0 (January) to 11 (December)
var year = now.getFullYear(); // Year as a 4 digit number
var hr = now.getHours(); // Hour, from 0 to 23
var min = now.getMinutes(); // Minute, from 0 to 59

1
document.write is not supported by many browsers if XHTML served as XML or XHTML.
LI-JavaScript (19/3/07) Page 9

var sec = now.getSeconds(); // Second, from 0 to 59


var timeStr = 'Time now is '+hr+':'+min+':'+sec+' on '+dayInMonth+'/'+
(month+1)+'/'+year;
A web page which displays the above ‘time string’ using the document.write method will
be found at http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex7-1.html.
Date objects store time internally as milliseconds since midnight, 1 January 1970. Using the
getTime and setTime methods which access these values, it’s possible to step backwards
and forwards from a given date and time, without having to deal with the complexities of the
varying number of days in a month or whether it’s a leap year or not. 1 The code fragment
below sets tomorrow to exactly one day from now:
var now = new Date();
var tomorrow = newDate();
tomorrow.setTime(now.getTime()+24*60*60*1000);
For an example of the use of date calculations, see
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex7-2.html.
It is also possible to create your own objects and ‘classes’ of object in JavaScript; see Section
13.

8 Arrays
Arrays are a kind of object. Unlike Java arrays, they are not of fixed size and are untyped.
Thus the following is legal:
var a = Array();
a[0] = 6;
a[2] = ' is ';
a[3] = true;
The value of a[1] is undefined. (The expression typeof a[1] will have the string value
'undefined'.) The length of the array, as determined by a.length, is 4.
Had a been declared by
var a = Array(5);
with the same statements following, then a[4] would also be undefined. It is not an error to
attempt to access undefined values in JavaScript; they generally behave as if they had the
value null (similar to Java’s null). We can show all the defined values in an array via code
such as:
function showArray(a)
{ var msg = '';
for (var i=0; i<a.length; i++)
{ if(a[i] != null) msg = msg + a[i];
}
alert(msg);
}
Notice the use of the function alert (strictly window.alert ). This takes a single string
argument and displays an alert box containing it. In this case the message will be ‘6 is true’.
Had if(a[i] != null) been omitted, the message would have been ‘6undefined is
trueundefined’.
(JavaScript’s automatic type conversion means that we could just write
if(a[i]) msg = msg + a[i] + '\n';
since the value of a[i] will be converted to true or false as required. Avoid this style! The
values 6, ' is ' and true contained in a will indeed be treated as true, while null or un-

1
Since JavaScript uses only real arithmetic, complex calculations involving integers such as times may yield
apparently incorrect results if care is not taken (e.g. by rounding result values).
Page 10 LI-JavaScript (19/3/07)

defined values are treated as false. However, if a[0] were set to 0 instead of 6, this would
also be treated as false and hence omitted from the output. Although this style is commonly
used by JavaScript authors, my advice is to avoid it.)
‘Array literals’ – values enclosed in square brackets and separated by commas – can be used
wherever arrays can. Their main use is in initialization:
var a = [6, , ' is ', true]; // a[1] is undefined.
A number of methods are predefined for arrays. These include concat, reverse and sort.
By default the latter sorts arrays alphabetically, converting if necessary. Thus the following
code generates the message ‘ is 6timetrue’.
var a = [6, 'time', ' is ', true];
a.sort();
var msg = '';
for (var i=0; i<a.length; i++)
{ if(a[i] != null) msg = msg + a[i];
}
alert(msg);

9 Functions
We have seen that functions can be defined in JavaScript using the keyword function. An
important difference from Java is that in JavaScript, functions are a data type just as are
numbers or strings. The following definitions of the function square are equivalent:
function square(x) { return x*x; }
var square = function (x) { return x*x; }
Each definition gives the variable square a function value. Function values can be assigned
like any other value. For example, this code displays the value 27:
function cube(x) { return x*x*x; }
cubeOf = cube;
alert(cubeOf(3));
JavaScript functions can be called without their full number of arguments – see the further
discussion in Section 11 below. Parameters are matched to arguments from the left; any
unmatched parameters are given the special undefined value.
It is extremely important to understand the difference between these two lines of code:
cubeOf = cube; // cubeOf is now a function.
cubeOf = cube(); // cubeOf now has the value NaN.
The first assigns the value of the variable cube (which is a function) to the variable cubeOf,
so that cubeOf is effectively the same function as cube. The second calls the function cube,
which will return NaN (since cube’s parameter x will be given the undefined value) and then
assigns this value to cubeOf.
This feature of JavaScript allows function values to be passed as arguments. For example, the
function map(f, a) defined below returns the array formed by applying the function f to
each of the elements of the array a.
function map(f, a) // f is a function, a is an array
{ var res = Array();
for (var i=0; i<a.length; i++)
{ res[i] = f(a[i]);
}
return res;
}
var a = [3, 10, -1, 5];
showArray(map(cube, a)); // showArray() was defined earlier
The values displayed will be 27, 1000, -1, 125.
LI-JavaScript (19/3/07) Page 11

10 Equality Tests
The JavaScript equality operator == applies automatic type conversions to its arguments; its
opposite is !=. Numbers, strings and booleans are then equal if they have equal values; even
objects may test as equal if there are valueOf or toString methods which can be used to
convert them to numbers, strings or booleans. Thus in the following JavaScript code, all of
the values successively assigned to the variable test will be true:
var s = 'This';
var test;
var x;
var y = new Object();
y.val = 'This';
y.toString = function() { return y.val }
test = 'This' == s; // Operands of same type; no type conversion.
test = 10 == '10';
test = false == 0;
test = x == null; // Note that x exists but is undefined.
test = y == s; // Its toString() method is applied to y.
JavaScript has another operator, === , which does not apply type conversions to its argu-
ments; its opposite is !==. If in the code above, == is replaced by ===, only the first assign-
ment to test yields true; all the others yield false.

11 Scope and Existence


The rules governing the scope of a variable – roughly the region of the program in which it is
available – differ very significantly between JavaScript and Java.
The scope of a JavaScript variable defined by a var statement is:
• The whole program, i.e. all the JavaScript used in the web page regardless of where it
appears, if the var statement appears outside a function definition.
• The whole of the function if the var statement appears in the function.
There is no block level scope in JavaScript.
If a variable is assigned a value but there is no corresponding var statement, then the variable
is always implicitly declared globally. This can cause considerable confusion. You are
strongly advised always to declare variables with a var statement either globally or locally to
a function.
Consider the following convoluted code (which should not be imitated!):
var i = 'global';
var j = 'global';
function display(i,j,k,l)
{ var msg = 'i = '+i+'\n';
msg += 'j = '+j+'\n';
msg += 'k = '+k+'\n';
msg += 'l = '+l+'\n'; // Point 1
alert(msg);
}
function test(i)
{ i = 'test';
var j = 'test';
k = 'test';
l = 'test';
}
display(i,j,k); // Point 2
test(i);
display(i,j,k,l); // Point 3
var k = 'global';
Page 12 LI-JavaScript (19/3/07)

This code demonstrates a number of features of JavaScript scoping:


• Functions can be called with the wrong number of arguments, as in the first call of the
function display above. Matching works from the left; un-matched parameters are
given the undefined value. Hence the alert at ‘Point 2’ above shows that i and j have the
value ‘global’, whereas k and l are displayed as ‘undefined’.
• There is a distinction between a variable’s existence and its value being undefined. At
‘Point 1’ above, the variable l exists (because it has been specified as the parameter of
the function display). However, in the first call of display it doesn’t have a value, so
is given the special undefined value. If we change the statement at ‘Point 2’ above to
display(i,j,k,l); // Point 2
then at this point l does not exist at all, since it has not been declared in a var statement
nor has it been assigned a value (nor is it a function parameter). Hence a JavaScript error
will be generated.
(Safari [2.0.4] produces the appropriate error message ‘ReferenceError – can’t find
variable: l’. Firefox [2.0.0.1] produces the error message ‘l is not defined’ which is
potentially misleading, since the error is not caused by l having the undefined value.)
You may wonder why the variable k exists at ‘Point 2’ but has the undefined value.
Since scope extends backwards, the statement var k = 'global' at the end of the
program has exactly the same meaning as writing var k at the very top of the program
and then k = 'global' at the end. The existence of k extends backwards; the assign-
ment of a value does not.
• Inside the function test , i and j are respectively a parameter and a locally declared
variable. Hence assigning them string values does not change the global variables of the
same name. The variable k is not declared inside test, but has been declared globally,
so k is the global variable. Hence assigning it a string value changes the global variable.
The variable l is not declared anywhere in the program, so the statement l = 'test';
is an implicit global declaration executed when the function is first executed.
The result is that at ‘Point 3’ in the program the values of i and j are still the string
‘global’, whereas l now exists and along with k has the value ‘test’.
The moral should be clear: don’t write code like this!
• Declare global variables at the top of the program with an explicit var statement,
initializing them where appropriate.
• Declare local variables in the same way.
Since JavaScript has no block level scoping, Java programmers need to be very careful with
loop variables. In code like the following, it is easy to forget that in JavaScript the loop
variable i is actually the global variable i, in spite of its apparently separate declaration:
var i = 6;
...
for (var i=0; i<10; i++) // The var is redundant and has no effect
{ ... }
// At this point, i has the value 10 not 6.
JavaScript programmers frequently want to test whether some entity exists and/or has a value
at a particular point in a program.
• The only test that is always safe for a variable is whether the typeof operator does not
return the string 'undefined', e.g. typeof x != 'undefined'. This yields true if the
variable exists, even if it has not been given a value, otherwise it yields false.
• Fields of existing objects can also be tested for inequality with null, e.g. window.x !=
null . This yields true if the field exists and has been given a value other than null .
Referencing a non-existent object will raise an exception; referencing a non-existent
field does not (provided its object exists). Hence the use of window.x here rather than x.
LI-JavaScript (19/3/07) Page 13

• A direct test on a field, as in if (window.x), which is commonly seen, yields true if the
field exists and has been given a value other than null, false, the empty string or 0.

12 Events and Event Handling


We have already seen the onclick attribute used with both images and buttons (Examples 1-1
and 1-2 above). When an item with this attribute is clicked by the user, by default the text
value of the attribute is treated as JavaScript and is executed by the browser. 1 In general the
value will contain a function call, although any valid JavaScript is equally possible.
There are some 20-odd predefined events with corresponding HTML attributes, although not
all will have event handlers supported by all browsers for all HTML elements.
It’s important to distinguish between the action taken by the event handler and the default
action taken by the browser. Consider the following HTML:
Example 12-1 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex12-1.html)
. . . . . . . . . .
function check()
{ return confirm('Go to the School of Computer Science web site?');
}
. . . . . . . . . .
<p>
<a onclick="return check();"
href="http://www.cs.bham.ac.uk">School of Computer Science</a>
</p>
When the user clicks on the link, the browser first executes the JavaScript return check().
The function check is called and uses the function confirm (a method of window) to display
a dialog asking the user to choose between the ok and cancel buttons. The value returned by
confirm is one of the boolean values true or false, which in turn is the value returned by
check . The result is that the onclick attribute returns true or false to the browser. For this
event handler, false causes the default browser action to be abandoned, so the link is not
followed.
Five of the most useful general events and handlers are shown in the table below.
Event Cause Effect of a return value
onchange User finishes entering text in a text box or
selects/deselects an item.
onclick User clicks once. If a link, false prevents it
being followed.
onload Item finishes loading (document or image
normally).
onmouseover Mouse moves over item. If a link, true prevents URL
being displayed in the status
bar.
onmouseout Mouse moves off item.
Example 12-2 uses all of these five events to create a web page which can evaluate a simple
arithmetic expression consisting of two numbers and an operator. I suggest you look at the
web page in operation before studying the source code and reading the notes below it.
Example 12-2 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex12-2.html)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type"

1
Strictly the attribute value should begin with javascript:.
Page 14 LI-JavaScript (19/3/07)

content="text/html; charset=ISO-8859-1"/>
<title>Example 12-2</title>
<script type="text/javascript">
var xBox; var yBox; var zBox; var msgBox;
// Initialize the global variables to the five text boxes
function init(xID, opID, yID, zID, msgID)
{ xBox = document.getElementById(xID);
opBox = document.getElementById(opID);
yBox = document.getElementById(yID);
zBox = document.getElementById(zID);
msgBox = document.getElementById(msgID);
}
// Reset the boxes
function reset()
{ xBox.value = 0; yBox.value = 0; zBox.value = 0;
opBox.value = '+'; msgBox.value = '';
}
// Calculate and display the result of x op y
function calculate()
{ x = parseFloat(xBox.value);
xBox.value = x;
y = parseFloat(yBox.value);
yBox.value = y;
op = opBox.value;
switch(op)
{ case '+': z = x + y; break;
case '-': z = x - y; break;
case '*': z = x * y; break;
case '/': z = x / y; break;
default: opBox.value = '+';
z = x + y; break;
}
zBox.value = Math.round(1000*z)/1000; // Show 3 d.p.
}
// Show a text message in the msgBox
function msg(msgText)
{ msgBox.value = msgText;
}
</script>
</head>
<body onload="init('left', 'op', 'right', 'res', 'note');">
<h1>Simple JavaScript calculator</h1>
<p>
<input id="left" type="text" size="7" value="0"
onchange="calculate();"
onmouseover="msg('Input a number.');"
onmouseout="msg('');"/>
<input id="op" type="text" size="2" value="+"
onchange="calculate();"
onmouseover="msg('Input one of +, -, *, /.');"
onmouseout="msg('');"/>
<input id="right" type="text" size="7" value="0"
onchange="calculate();"
onmouseover="msg('Input a number.');"
onmouseout="msg('');"/>
=
<input id="res" type="text" size="15" value="0" disabled="disabled"/>
<input type="button" value="Reset"
onclick="reset();"/>
<br/>
<input id="note" type="text" size="30" value="" disabled="disabled"/>
</p>
</body>
</html>
LI-JavaScript (19/3/07) Page 15

Some notes on this example:


• In the interests of modularity it is desirable to de-couple HTML and JavaScript code as
far as possible. In particular, it is much easier to make changes if the values of id attrib-
utes are not ‘hard coded’ into the JavaScript. One way of achieving this is to use an
initialization function, like init in Example 12-2, which passes the necessary id values
to the JavaScript program. The onload attribute of the body element causes the
corresponding event handler to be called only after the body is fully loaded, thus ensur-
ing that all elements have been created before the JavaScript code attempts to access
them.
• When the mouse is over the first three input elements, their onmouseover and
onmouseout attributes cause an informative message to appear in the "note" input el-
ement; it disappears when the mouse moves away.
• The "res" and "note" input elements are disabled to prevent user input as they are only
used to display text. Browsers automatically refresh the contents of form elements when
their values change, unlike ‘normal’ HTML elements which are static once displayed.
• The onchange attribute of the first three input elements ensures that when the user
changes them, the result value is changed via a call to calculate. The user interface is
slightly awkward because the user must move away from the element to trigger the
onchange event. In general this is not as useful an event as it may seem.
• The value obtained from an input element is a string. Rather than use automatic type
conversion (which will work only if the syntax of the string is exactly correct), the built-
in parseFloat function has been used. This is more tolerant of incorrect input (e.g.
'34.1a' will be converted to 34.1). Notice that the value produced after conversion is
re-displayed so the user knows what number has been accepted.

13 Creating Objects
There are a number of different ways of creating your own objects in JavaScript. Only one
approach is discussed here.
Although you will see examples of JavaScript programming in which objects and construc-
tors are used in what appears to be Java-like fashion, in reality this approach is of consider-
ably less value:
• In Java, classes and class-based inheritance are used to create a compatible and safe set
of types. JavaScript’s untyped variables make this irrelevant.
• Classes and their instances in Java allow information hiding to be enforced, e.g. by
declaring fields and methods to be private. JavaScript objects are completely open. If
data is stored in the fields of objects, access methods are only useful when consistency
between fields must be maintained.
Individual objects can be created by the new operator applied to the class Object:1
var myObject = new Object();
The required fields and methods of the newly created object can then be set up. They can be
added to at any time after the object has been created.
myObject.age = 32;
myObject.name = 'John';
myObject.toString = function () { return myObject.name+' '+myObject.age; };
Given this code, the statement alert(myObject) will display ‘John 32’, the toString
method being called as a result of automatic type conversion (alert expects its argument to
be a string).

1
Parentheses after Object are optional.
Page 16 LI-JavaScript (19/3/07)

Typically we want to create more than one object of a kind. One solution is to create a
‘factory’ or ‘make’ method that creates and returns objects:
function makeRect(width,height)
{ var r = new Object();
r.w = width;
r.h = height;
r.toString = function() { return r.w+' by '+r.h; };
return r;
}
rect1 = makeRect(10,45);
rect2 = makeRect(100,10);
alert(rect1); // Will display '10 by 45'
As noted above, we could create accessors like getWidth or setWidth, but as there is no
way of preventing access to the fields of the objects, this would produce no real gain at the
expense of inefficiency.1
The slight problem with this approach is that every object created by makeRect will contain
identical copies of its methods, which is an inefficient use of memory. This is only likely to
become a problem when creating a large number of objects with substantial method definit-
ions.
An alternative is to create a single global function and store a reference to it in the object.
Because the variable r will not be available globally (it is local to makeRect), we need to use
the special variable this which refers to the originating object of a method:
function _rectToString()
{ return this.w+' by '+this.h;
}
function makeRect(width,height)
{ var r = new Object();
r.w = width;
r.h = height;
r.toString = _rectToString;
return r;
}
var rect1 = makeRect(10,45);
alert(rect1); // Displays '10 by 45'.
(Since _rectToString is now global, it could be called directly; giving it a name starting
with an underscore is a useful convention to show that it is not meant for independent use.)
Re-use can be achieved by writing a new ‘make’ method which adds or changes fields based
on an existing object type. For example, to create filled rectangle objects, we can start with a
rectangle object:
function makeFilledRect(width,height,colour)
{ r = makeRect(width,height);
r.colour = colour;
r.toString = function ()
{ return this.w+' by '+this.h+'; colour '+this.colour;
};
return r;
}
var rect2 = makeFilledRect(11,56,'green');
alert(rect2); // Displays '11 by 56; colour green'.
JavaScript has ‘object literals’: a list of field name and value pairs separated by commas. The
statement:
r = { w: 10, h: 25, colour: 'green' };

1
Information hiding can be achieved in JavaScript by using local variables to store data, an approach not
covered in this module. See my web page on Information Hiding in JavaScript.
LI-JavaScript (19/3/07) Page 17

creates an object r with fields w, h and colour.

14 Manipulating CSS with JavaScript


One of the interesting uses of JavaScript is to manipulate the style of HTML elements
dynamically.
JavaScript creates a style object for each element and makes it a field of the object repre-
senting that element. The style object in turn has fields. Important ones are cssText and
fields corresponding to each applicable CSS property. Capitalization is used in field names in
place of the hyphens in CSS properties. Thus for example border-left-width becomes
borderLeftWidth. Note that the style object only holds the styles explicitly declared for that
element, not those inherited via class attributes.
Example 14-1 shows a simple example of dynamic CSS. As with earlier examples, I suggest
you look at the web page in operation before studying the source code and reading the notes
below it.
Example 14-1 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex14-1.html)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1"/>
<title>Example 14-1</title>
<style type="text/css">
.box
{ border-width: 2px;
border-style: solid;
border-color: black;
width: 100px;
height: 100px;
}
</style>
<script type="text/javascript">
var box1;
var goRed = true;
function changeColour()
{ if(goRed) box1.style.borderColor = 'red'
else box1.style.borderColor = 'black';
goRed = !goRed;
}
function init(boxID)
{ box1 = document.getElementById(boxID);
setInterval('changeColour()',1000);
}
</script>
</head>
<body onload="init('box1');">
<p>The box below should change colour regularly from black to red and
back again.</p>
<div id="box1" class="box"/>
</body>
</html>
Some notes on this example:
• The style attached to all elements of class box causes them to have a 2 pixel wide black
border all round. In the example, this style is attached to a div with id "box1".
Page 18 LI-JavaScript (19/3/07)

• The function init sets the value of the global variable box1 to the JavaScript object
representing the div. The setInterval function is then used for animation (see the dis-
cussion below); changeColour will be called once every second.
• The function changeColour then sets and re-sets the border-color style specifically
for the div element, which by the rules of CSS, over-rides the class-wide style. This
relies on the browser dynamically changing its display whenever the CSS changes.
JavaScript provides three useful ‘timer’ functions (methods of the window).
• setInterval(JavaScript_string, time_in_msec) executes the JavaScript given in
the first string argument after every interval of time_in_msec milliseconds. It returns a
timer identifier which should be saved if it will be necessary to stop the activity.
• clearInterval(timerID) stops the action created by setInterval:
timer1 = setInterval('run()', 2000);
. . . . . .
clearInterval(timer1);
• setTimeout(JavaScript_string, time_in_msec) executes the JavaScript given in
the first string argument after one interval of time_in_msec milliseconds. (If the action
has not taken place, it can be stopped via clearTimeout(timerID).)
In Section 13, we discussed creating objects and in particular rectangle objects. By combin-
ing this with the technique discussed here we can ‘draw’ the rectangles as they are created.
We first set up the CSS box class to ensure that a div of this class will not be visible:
<style type="text/css">
div.box
{ position: fixed;
top: 0px; left: 0px;
width: 0px; height: 0px;
border: solid 0px transparent;
}
</style>
Notice that we declare position: fixed; to ensure that the element is positioned at an
absolute location relative to the window.1 Now we write the JavaScript to create rectangle
and filled rectangle objects, defining their size, position and colour via CSS styles. Note how
we store a reference to the corresponding DOM object within our rectangle object so that it is
easy to access the style field.
function makeRect(id,left,top,width,height)
{ var r = new Object();
r.left = left;
r.top = top;
r.right = left+width;
r.bottom = top+height;
r.domObj = document.getElementById(id);
r.domObj.style.cssText += 'left:'+left+'px; top:'+top+'px; width:'+
width+'px; height:'+height+'px; border: solid 2px black;';
return r;
}
function makeFilledRect(id,left,top,width,height,colour)
{ r = makeRect(id,left,top,width,height);
r.colour = colour;
r.domObj.style.cssText += ' background-color: '+colour;
}
The function init is used to set up three rectangles when the body of the page has loaded:

1
Older versions of Internet Explorer are notorious for their poor support of the CSS position property.
LI-JavaScript (19/3/07) Page 19

function init(box1ID,box2ID,box3ID)
{ makeFilledRect(box1ID,15,60,50,100,'green');
makeRect(box2ID,30,100,150,75);
makeFilledRect(box3ID,45,85,50,50,'red');
}
Finally we set up the initially invisible div elements in the body of the page:
<body onload="init('box1','box2','box3');">
<div id="box1" class="box"></div>
<div id="box2" class="box"></div>
<div id="box3" class="box"></div>
</body>
The complete page will be found at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex14-2.html.
If we want the user to interact with our objects, then it is useful to create links in both direc-
tions between our object and the corresponding DOM object. JavaScript’s ability to add
fields to existing objects makes this easy to do, although we must be careful not to over-ride
an existing field. The makeRect function can have an additional line added:
function makeRect(id,left,top,width,height)
{ var r = new Object();
. . . . .
r.domObj = document.getElementById(id);
. . . . .
r.domObj.owner = r; // Relies on 'owner' being a new field!
return r;
}
As a simple of example of how this link can be used, suppose we want to change the
appearance of a rectangle object when the user clicks on it. We begin by adding a clicked
field and a doClick method to makeRect:
function makeRect(id,left,top,width,height)
{ var r = new Object();
. . . . .
r.clicked = false;
r.doClick = function ()
{ if (!r.clicked) r.domObj.style.borderColor = 'yellow';
else r.domObj.style.borderColor = 'black';
r.clicked = !r.clicked;
};
return r;
}
Finally we add an onclick event handler to the div elements. In the event handler code which
is the value of an event attribute, the keyword this refers to the DOM object corresponding
to the HTML element. Hence by using this and the owner field we have added to it, we can
link to the corresponding rectangle object:
<div id="box1" class="box" onclick="this.owner.doClick()"></div>
<div id="box2" class="box" onclick="this.owner.doClick()"></div>
<div id="box3" class="box" onclick="this.owner.doClick()"></div>
The complete page will be found at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex14-3.html.
For a further example of what it is possible to do with JavaScript and dynamic CSS, see
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex14-4.html. (Depending on your machine
and browser, the animation speed may or may not be satisfactory.)
Page 20 LI-JavaScript (19/3/07)

15 Manipulating the HTML DOM


Consider the following XHMTL:
<div>
<p>Manipulating the JavaScript DOM is a <b>very</b> important
technique.</p>
<p>Varying browser support is the main problem.</p>
<div>
Like all XML, it can be represented as a tree of nodes. In the diagram below, note that the
limited width of the page has prevented me from aligning all the grandchildren of the div
correctly; also spaces and newlines are shown explicitly in the values of text nodes.
div

#text p #text p #text

'\n••' '\n••' #text '\n'

'Varying
#text b #text browser•sup
port•is
the•main•pr
'Manipulatin '•important\n oblem.'
g•the•JavaS #text •••••techniqu
cript•DOM•i e'
s•a•' 'very'

Usually we don’t want the text nodes which represent only ‘white space’, but must be
prepared for them to be present.1
The browser will create a JavaScript object for each node, including the text nodes, as part of
the DOM. Each node has some standard fields and methods. To move through the tree, the
following fields can be used:
• parentNode contains the parent of the node.
• childNodes contains an array of the child nodes.
• firstChild, lastChild provide immediate access to the first and last child nodes.
• nodeName holds the name of the node as a string (e.g. 'div', 'b' or '#text').
• data holds the text of a text node, which have no children (hence the dotted lines in the
figure above).
Thus the code below should display the string ‘very’:
var theDiv = document.getElementById('div01');
alert(theDiv.childNodes[1].childNodes[1].firstChild.data);
In practice, it’s rarely desirable to work through the tree in this way because the exact
number of text nodes (and hence child nodes) depends on the way the document was laid out
and on how the browser handles text and white space, so the shape of the tree is not readily
predictable.

1
Browsers may also split long blocks of text among adjacent text nodes, although sibling text nodes ought to
be merged together according to the W3C standard.
LI-JavaScript (19/3/07) Page 21

Better approaches are:


• To give elements unique id attributes and use node.getElementById(id_value) to
return the object with id = id_value in the tree starting at node.1
• To use node.getElementsByTagName(element_name) to return an array of all objects
in the tree starting at node whose nodeName is the string element_name.
Finding a node is usually a precursor to changing it (or more accurately one or more of its
children). As with navigating the DOM, there are many methods and fields which can be
used to make changes. The following are perhaps the most useful:
• document.createElement(element_name) creates an element object whose nodeName
is given by the string element_name.
• node.appendChild(newNode) adds newNode as node’s last child.
• node.insertBefore(newNode,oldNode) inserts newNode immediately before oldNode,
both being children of node.
• node.replaceChild(newNode,oldNode) relaces node’s child oldNode by newNode.
• node.removeChild(oldNode) removes node’s child oldNode.
• innerHTML is a string field representing the HTML ‘inside’ the node; giving it a new
value is an easy way of changing the text belonging to a node, avoiding the need to
create text nodes and add/replace them as children.2 (This field should not normally be
used to add complex HTML to a node, because it does not immediately create the corre-
sponding nodes in the DOM.)
• node.setAttribute(attr_name, attr_value) adds an attribute to node whose
nodeName is given by the string attr_name and whose nodeValue is given by the string
attr_value.
Given the HTML discussed above, the code in the following rather artificial example
repeatedly changes the bold word ‘very’ in the first paragraph to ‘somewhat’ and also
replaces the second paragraph.
Example 15-1 (http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex15-1.html)
. . . . . . . . . .
var words = ['very', 'somewhat'];
var iWord = 0;
var theDiv;
var firstB;
var secondP;
var newP;
function init()
{ // Now the document has loaded, set up the required objects.
theDiv = document.getElementById('div01');
firstB = theDiv.getElementsByTagName('b')[0];
secondP = theDiv.getElementsByTagName('p')[1];
newP = document.createElement('p');
newP.innerHTML = 'One problem is varying browser support.';
// Alternate the text of the paragraphs.
setInterval('changeText()',1000);
}
function changeText()
{ iWord++; if(iWord>1) iWord = 0;
firstB.innerHTML = words[iWord];
if (iWord==1) theDiv.replaceChild(newP,secondP)
else theDiv.replaceChild(secondP,newP)
}
. . . . . . . . . .

1
This seems to be more browser compatible if node is document.
2
However, setting innerHTML does not work for XHTML served as XML on some browsers, e.g. Safari 2.0.
Page 22 LI-JavaScript (19/3/07)

<body onload="init();">
<div id="div01">
<p>Manipulating the JavaScript DOM is a <b>very</b> important
technique.</p>
<p>Varying browser support is the main problem.</p>
</div>
</body>
The ability to create new HTML elements and attributes enables us to re-write Example 14-2.
In the original version, the div elements which became boxes are explicitly coded in the
HTML, with an initial style which makes them invisible. Then when each rectangle object is
created, the style of the corresponding div is changed. This approach requires the number of
boxes which will be created to be known in advance.
The alternative is to create the divs dynamically. Compare the version of makeRect below
with that given above.
var theBody; // Must be initialized to the <body> element.
. . . . . . . . . .
function makeRect(left,top,width,height)
{ var r = new Object();
r.left = left;
r.top = top;
r.right = left+width;
r.bottom = top+height;
r.domObj = document.createElement('div');
r.domObj.setAttributeNode('class','box');
theBody.appendChild(r.domObj);
r.domObj.style.cssText += 'left:'+left+'px; top:'+top+'px; width:'+
width+'px; height:'+height+'px; border: solid 2px black;';
r.clicked = false;
r.domObj.onclick = function ()
{ if (!r.clicked) r.domObj.style.borderColor = 'yellow';
else r.domObj.style.borderColor = 'black';
r.clicked = !r.clicked;
};
return r;
}
Note the following points:
• The global variable theBody must be initialized to the body element object, e.g. by
theBody = document.getElementsByTagName('body')[0];.
• The set of statements in bold then create a div of the form <div class="body" />.
• We can bypass the need to create an onclick attribute for the div by directly setting the
onclick method in the corresponding object. The DOM object now doesn’t need to link
to the rectangle object; clicking on the div will automatically call the right event handler.
The complete HTML page is at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex15-2.html. For a larger example of what
can be done with JavaScript and dynamic changes to both CSS and the HTML DOM, see
http://www.cs.bham.ac.uk/~pxc/langinf/FifteenPuzzle.html.
LI-JavaScript (19/3/07) Page 23

16 Loading and processing XML


A set of techniques involving ‘asynchronous JavaScript technology and XML’ (AJAX) has
become popular since about 2005. The following diagram shows the basic four steps
involved.1
Client Side Server Side

Browser
2
HTTP Request
XMLHttpRequest
Web Server
Associated
callback
function XML Document
3
4
JavaScript Page
Call updates

Database,
Page or User via other program,
User Interface etc.

The numbers in the diagram correspond to the steps:


1. The action is initiated by the web page or by a user generated event.
2. An XMLHttpRequest object is invoked. It is given a URL and a ‘callback’ function.
Browsers provide this object in different ways, but the core functionality is the same.
The URL is used to generate an HTTP request, addressed to the same web server which
supplied the HTML page. The request is asynchronous: once initiated it runs
independently. Thus while waiting for the XML document to load, the browser can
continue to respond to other user actions, e.g. scrolling, or to execute other JavaScript.
3. Based on the URL in the request, the web server returns an XML document. The simpl-
est kind of URL would give the location of an XML file. A more plausible URL would
initiate a server-side program which generates XML, perhaps based on data acquired
from a database.
4. When the XMLHttpRequest object has correctly received all of the XML document and
it has been converted to a DOM object, it executes the callback function it was given.
This function typically takes the XML document object as its argument and uses it to
update the HTML page by manipulating the HTML document object, as discussed in
Section 15.
There are an important security restrictions imposed by browsers such as Firefox:
• An XMLHttpRequest object can only make requests via the http: protocol. This
ensures that web pages cannot read files on the local computer via the file: protocol.

1
Redrawn from http://java.sun.com/developer/technicalArticles/J2EE/AJAX/IntroAjaxPageAuthors.html.
Page 24 LI-JavaScript (19/3/07)

• The requested file must be served from the same domain as the web page, e.g. the web
page reached via http://www.myname.co.uk/... can only request XML files whose
URL also begins with http://www.myname.co.uk/.
Some code needed to evoke an XMLHttpRequest object will be found in
http://www.cs.bham.ac.uk/~pxc/langinf/xhrlib.js. I have tried to write code which traps as
many errors as possible – it’s not designed for ‘real’ use. It’s not necessary to understand the
details of the code in this module.
The key to using this library code is the function requestXML(url, processor). This
function:
• generates an XML HTTP request for the URL contained in the string supplied as its first
argument, url
• when the XML has been loaded, passes the XML DOM object created by the browser to
the function passed as its second argument, processor.
The URL supplied must either be:
• relative to the web page (e.g. ../mydata.xml)
• a full URL starting with http:// and the same domain name as the web page.
The function passed as the second argument must take one parameter. When the XML is
loaded, this parameter will be set to the XML DOM object created by the browser. The
function must then process the DOM object appropriately.
A simple example can be used to show how this works. We begin with the XML document
stored at http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/data.xml:
<?xml version="1.0"?>
<!DOCTYPE data
[
<!ELEMENT data (person*)>
<!ELEMENT person (name, birthplace)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT birthplace (#PCDATA)>
]>
<data>
<person>
<name>Arjun Sen</name>
<birthplace>Birmingham</birthplace>
</person>
<person>
<name>John Smith</name>
<birthplace>London</birthplace>
</person>
<person>
<name>Mary Jones</name>
<birthplace>Cardiff</birthplace>
</person>
</data>
The task is to construct a simple HTML page which loads this file and displays names and
birth places.
The body of the page just has a heading and an empty div element.
<body onload="init('display');">
<h1>People and their Places of Birth</h1>
<div id="display"></div>
</body>
The init function sets up a JavaScript variable displayDiv to hold the DOM object corre-
sponding to the empty div element, and then initiates the asynchronous request for an XML
document, using the requestXML function from the xhrlib.js file. Note that because a relative
URL has been used in the call of requestXML, it will be resolved to the same location as the
LI-JavaScript (19/3/07) Page 25

web page, and in particular the same domain. It is essential that the web page is opened via
the http: protocol and not the file: protocol, i.e. the web page must be on a web server.
<script type="text/javascript" src="../../xhrlib.js"></script>
<script type="text/javascript">
var displayDiv;
function init(displayDivID)
{ displayDiv = document.getElementById(displayDivID);
requestXML('data.xml', doDisplay);
}
Because the request is asynchronous, init will terminate after the call to requestXML; it will
not wait for the file to be loaded. When the file has been loaded, the JavaScript engine will
call the function doDisplay, with the XML document object as its argument.
The idea is to walk through the XML document object, creating a new HTML p element (i.e.
a paragraph) for each person in the XML file. Each paragraph is then made a new child of the
empty ‘display’ div.
The steps involved are:
• Use getElementsByTagName('person') to get an array of the objects corresponding to
the person elements in the XML document.
• Iterate through the array extracting the text from the name and birthplace elements of
each person element. Assume that the XML is valid, i.e. that each person element has
only one name and one birthplace element and that the first child element of each is the
text we want.
• Create a new paragraph whose inner HTML is the extracted text and add it to the end of
the ‘display’ div.
This produces the following code.
function doDisplay(xmlDoc)
{ var persons = xmlDoc.getElementsByTagName('person');
for (var iPerson = 0; iPerson < persons.length; iPerson ++)
{ var person = persons[iPerson];
var txt = person.getElementsByTagName('name')[0].firstChild.data;
txt += ', ';
txt +=person.getElementsByTagName('birthplace')[0].firstChild.data;
var newP = document.createElement('p');
newP.innerHTML = txt;
displayDiv.appendChild(newP);
}
}
The complete HTML page is at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex16-1.html. An improved alternative in
which the data is displayed in a table, constructed without using the innerHTML field, will be
found at http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex16-2.html.
A more realistic example would display only part of the data. For example, if the user enters
a name the page should respond by showing the person’s place of birth. Extending Example
16-1 to achieve this is straightforward. The body of the page is defined as:
<body onload="init('display','name');">
<h1>People and their Places of Birth</h1>
<p>
Enter person's name:
<input id="name" type="text" size="20" value=""/>
<input type="button" value="Show Place of Birth"
onclick="showBirthplace();"/>
</p>
<div id="display"></div>
</body>
Page 26 LI-JavaScript (19/3/07)

The user enters a name into the text input box and then presses the “Show Place of Birth”
button. The initially empty “display” div element is then used to show the result.
The init function sets up the necessary objects and requests the XML document. The call-
back function extracts and stores an array of the objects corresponding to the person
elements:
var displayDiv;
var nameInput;
var persons;
function init(displayDivID,nameInputID)
{ displayDiv = document.getElementById(displayDivID);
nameInput = document.getElementById(nameInputID);
requestXML('http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/'+
'data.xml', setupPersons);
}
function setupPersons(xmlDoc)
{ persons = xmlDoc.getElementsByTagName('person');
}
Finally when the user presses the button, the array is searched for the appropriate name and a
result displayed. A minor issue is that, as noted in Section 15, text values retrieved from an
XML source may contain extra white space. For this reason, xhrlib.js also contains
normalize_space(str) which normalizes a string by stripping any opening and closing
white space and replacing all other occurrences of white space by a single space.1
function showBirthplace()
{ var name = normalize_space(nameInput.value);
if (name != '')
{ var i = 0;
var found = false;
while (!found && i < persons.length)
{ var person = persons[i];
found = normalize_space(person.getElementsByTagName('name')[0].
firstChild.data) == name;
i++;
}
if (!found)
{ displayDiv.innerHTML = 'Unknown person: '+name+'.';
}
else
{ var birthplace = normalize_space(person.
getElementsByTagName('birthplace')[0].
firstChild.data);
displayDiv.innerHTML = name+' was born in '+birthplace+'.';
}
}
}
The complete HTML page is at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex16-3.html.
In spite of the fact that it only displays part of the data, Example 16-3 still downloads the
entire XML file. This is clearly undesirable:
• The file may potentially be very large and so its loading will cause an unacceptable
delay, especially with a slow connection.
• Since the XML file must be available by HTTP, there is no way of hiding data which the
user should not be able to access – by inspecting the source code of the JavaScript, the
user can find the URL of the XML file, and then download it directly.

1
Using the prototype field of the String object, xhrlib.js also attaches normalize_space to all strings, so
that normalize_space(str) and str.normalize_space() are then equivalent.
LI-JavaScript (19/3/07) Page 27

The solution is server-side processing. For example, the URL used to access the required
XML could be directed at a web page containing server-side script, written in a language
such as PHP. The web server executes such script before sending the result to the client. For
example, given a person’s name, the server could send back some XML describing only that
person. (The original data does not have to be stored as XML, since the program or script
running on the server can obtain data from any source and then ‘wrap’ it in XML.)
When http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/data.php?name=John Smith is
accessed,1 it returns XML equivalent to:
<person>
<name>John Smith</name>
<birthplace>London</birthplace>
</person>
Given a name other than “John Smith”, “Arjun Sen” or “Mary Jones”, it returns the name it
was given with birthplace as “unknown”.
The JavaScript below demonstrates how an HTML page might interact with this PHP page.
Notice that the HTTP request now occurs only when specific data is required.
var displayDiv;
var nameInput;
function init(displayDivID,nameInputID)
{ nameInput = document.getElementById(nameInputID);
displayDiv = document.getElementById(displayDivID);
}
var name;
function showBirthplace()
{ name = normalize_space(nameInput.value);
requestXML('http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/'+
'data.php?name='+name, displayBirthplace);
}
function displayBirthplace(xmlDoc)
{ var birthplace = normalize_space(xmlDoc.
getElementsByTagName('birthplace')[0].
firstChild.data);
if (birthplace == 'unknown')
displayDiv.innerHTML = 'The birthplace of '+name+'is unknown.';
else
displayDiv.innerHTML = name+' was born in '+birthplace+'.';
}
The complete HTML page is at
http://www.cs.bham.ac.uk/~pxc/langinf/2006/Exs/Ex16-4.html.
(Server-side processing with Java will be covered in the Software System Components
modules in Year 2.)
AJAX has significant advantages over other techniques for dynamically displaying data:
• It increases the separation between content and presentation. Content is transferred
between the server and browser as XML. The web page only deals with presentation and
user interaction.
• Changes to the page are ‘smooth’, at least compared to the alternative approach in which
changes to content are handled by loading a different web page.
• It utilizes the advantages of XML as a medium for data transfer (e.g. high compatibility
across platforms through its well-standardized text-based format; increased security
compared to binary data; ease of processing through the XML DOM which closely
parallels the HTML DOM).

1
Strictly the space in the URL should be escaped as %20.

You might also like