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
  References/Bibliography

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 reasonably 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:

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 resulted in a language called ECMAscript, although the name never caught on. The ECMA standard 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:

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-0 (http://www.cs.bham.ac.uk/~pxc/infoweb/2007/Egs/Eg1-0.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:

Adopting this approach, we can re-write Example 1-0 as follows.

Example 1-1 (http://www.cs.bham.ac.uk/~pxc/infoweb/2007/Egs/Eg1-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" src="Eg1-1.js"></script>
  </head>
  <body>
    <h1>Example 1-1</h1>
    <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>

Code loaded from a file via the src attribute of a script element is treated exactly as if it was written out in full in the same place. Notice that I deliberately didn't use the empty element form <script ... />. Although this should have exactly the same meaning, many browsers at present don't handle such forms properly.

The file Eg1-1.js then contains the 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
  document.images[imgNo].src = pic[i].src
}

The other piece of JavaScript in the example is the value step(0) of the onclick attribute of the img element.[2] This has to remain embedded in the HTML document, but won't cause a validity problem as it doesn't contain any characters which are invalid in XML/XHTML. In general, it's better to put as little JavaScript as possible into the body of an HTML page. A useful rule of thumb is to have only a single JavaScript statement in attribute values or script elements in the body; more than this should be moved to an external file and put into 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).

The DOM is needed again to ensure that the expression window.document.images[imgNo] inside the function step[3] 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.[4]

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/infoweb/2007/Egs/Eg1-2.html)

JavaScript change:

function step(imgID)
{ i++;
  if(i>1) i=0
  document.getElementById(imgID).src = pic[i].src
}

XHTML change:

  <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,[5] JavaScript has a similar syntax to Java. For programmers familiar with Java, this is both an advantage and a disadvantage.

Some differences between Java and JavaScript syntax:

3 Data Types in JavaScript

A major difference between JavaScript and Java is that JavaScript variables are not of a fixed type. In particular:

var count = 0;      // declared and initialized to a number value
count = count + 1;
count = 'Error: too many values entered.'; // given a string value
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 converted 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 composite 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:

Consider the following schematic code (where material in angle brackets isn't JavaScript).

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 another 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:

var s1 = 'Hello!';
var s2 = 'Hello!';
var s3 = s1;

all of the following expressions are true:

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.

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 window object can be omitted. Thus instead of the full expression 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:

'Console logging' in Firebug is also extremely useful in developing and debugging JavaScript. See http://getfirebug.com/logging.html.


JavaScript offers two methods of accessing the fields of objects.

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/infoweb/2007/Egs/Eg1-3.html.)

Objects can have methods (i.e. functions). A useful method of the document object is write. When executed it inserts output into the HTML source at the point at which it appears. This will only cause a change in the page displayed if the generated HTML is then processed by the browser. (Although document.write is easy to use, there's no way of validating the (X)HTML it generates. For this reason, many browsers don't support document.write in pages served as XML or XHTML. A better approach, although it's more complex, is to use functions which add to or alter the underlying DOM, as discussed in Section 15. Such functions are designed to make it impossible to create invalid output.)

XHTML 1.0 Strict has some important restrictions on where script elements, and hence document.write, can be placed. For example, table and tr elements cannot contain script elements, so a document.write can only generate either a whole table or the contents of a single cell.

Suppose 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
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/infoweb/2007/Egs/Eg7-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.[7] 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/infoweb/2007/Egs/Eg7-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 undefined 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.

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 arguments; its opposite is !==. If in the code above, == is replaced by ===, only the first assignment 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:

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';

This code demonstrates a number of features of JavaScript scoping:

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 assignment of a value does not.

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!

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.

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.[8] 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 example.

Example 12-1 (http://www.cs.bham.ac.uk/~pxc/infoweb/2007/Egs/Eg12-1.html)

JavaScript:

    function check()
    { return confirm('Go to the School of Computer Science web site?');
    }

HTML:

    <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/infoweb/2007/Egs/Eg12-2.html)

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()
{ var x = parseFloat(xBox.value);
  xBox.value = x;
  var y = parseFloat(yBox.value);
  yBox.value = y;
  var 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 a message in the msgBox
function msg(msgText)
{ msgBox.value = msgText;
}

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 12-2</title>
    <script type="text/javascript" src="Eg12-2.js"></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>

Some notes on this example:

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 constructors are used in what appears to be Java-like fashion, in reality this approach is of considerably less value:

Individual objects can be created by the new operator applied to the class Object:[9]

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).

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.[10]

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 definitions.

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' };

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 representing 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/infoweb/2007/Egs/Eg14-1.html)

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);
}

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">
    div.box
    { border-width: 2px;
      border-style: solid;
      border-color: black;
      width: 100px;
      height: 100px;
    }
    </style>
    <script type="text/javascript" src="Eg14-1.js"></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:

JavaScript provides three useful 'timer' functions (methods of the window).

timer1 = setInterval('run()', 2000);
. . . . . . 
clearInterval(timer1);

In Section 13, we discussed creating objects and in particular rectangle objects. By combining 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.[11] 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:

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/infoweb/2007/Egs/Eg14-2.html.

If we want the user to interact with our objects, then it is useful to create links in both directions 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/infoweb/2007/Egs/Eg14-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/infoweb/2007/Egs/Eg14-4.html. (Depending on your machine and browser, the animation speed may or may not be satisfactory.)

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 following diagram, 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.

Usually we don't really want the tree to contain text nodes which represent only 'white space', but we must be prepared for them to be present.[12]

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:

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.

Better approaches are:

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:

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/infoweb/2007/Egs/Eg15-1.html)

JavaScript:

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)
}

HTML:

  <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 following version of makeRect 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 complete HTML page is at
http://www.cs.bham.ac.uk/~pxc/infoweb/2007/Egs/Eg15-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/infoweb/FifteenPuzzle.html.

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.[15]

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 facility in a number of 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 simplest 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:

Some code needed to evoke an XMLHttpRequest object will be found in http://www.cs.bham.ac.uk/~pxc/infoweb/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:

The URL supplied must either be:

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/infoweb/2007/Egs/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 below sets up a JavaScript variable displayDiv to hold the DOM object corresponding 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 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.

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:

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/infoweb/2007/Egs/Eg16-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/infoweb/2007/Egs/Eg16-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>

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 callback 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/infoweb/2007/Egs/'+
             '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.[16]

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/infoweb/2007/Egs/Eg16-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 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/infoweb/2007/Egs/data.php?name=John Smith is accessed,[17] 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/infoweb/2007/Egs/'+
             '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/infoweb/2007/Egs/Eg16-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:

References/Bibliography

See the other handouts for this module, which are available online.

Footnotes

[1] For a more detailed explanation, see the section on validity in my Summary of Core XHTML.

[2] 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.

[3] 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.

[4] This approach is particularly likely to fail with older browsers.

[5] Until its release, it was apparently called LiveWire or LiveScript.

[6] 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 conversions are important in understanding how JavaScript works. It's thus slightly misleading when JavaScript is said to be an untyped language.

[7] 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).

[8] Strictly the attribute value should begin with javascript:.

[9] Parentheses after Object are optional.

[10] 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.

[11] Older versions of Internet Explorer are notorious for their poor support of the CSS position property.

[12] 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.

[13] This seems to be more browser compatible if node is document.

[14] However, setting innerHTML does not work for XHTML served as XML on some browsers, e.g. Safari 2.0.

[15] Redrawn from http://java.sun.com/developer/technicalArticles/J2EE/AJAX/IntroAjaxPageAuthors.html.

[16] 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.

[17] Strictly the space in the URL should be escaped as %20.

GoHome Page for "Information and the Web"