JavaScript tutorial

Navigation

Skip navigation.

Site search

Site navigation

JavaScript tutorial

Printing

Other tutorials

Table of contents

Important guidelines

First let's learn some important stuff. There are lots of browsers out there that cannot support JavaScript. Although browsers generally support more and more with each release, the language itself keeps evolving, and there is already a vast amount of the JavaScript language available for browsers to support. No browser will ever support all of it, and you cannot expect them to.

There are many reasons why people cannot or will not 'upgrade' to your chosen browser. A few are:

Whatever their reasons, you should not stop them from using your site. If your site uses JavaScript for navigation or content, then that navigation and content should be there without JavaScript enabled. Your JavaScript can extract content from the page, and do whatever it wants with that, but you should make sure that content is available without the script. If you want to use a script in way that you cannot simply extract the information from the static page, then you should use <noscript> to provide an alternative for people that cannot run your script.

There are also many browsers out there that you do not realise exist. I know of well over a hundred different browsers that support JavaScript to varying degrees. If your script uses something that browsers may not support, you should detect if the browser is capable of using it, and then fall back to an accessible alternative if it doesn't.

There is no magic formula for this, but the basic rules are that you should not detect a browser make or version. You will get it wrong, and you will mistakenly detect one browser and think it is another, ending up with you using the wrong code. You will make incorrect assumptions about current and future browsers, and you will cause problems for your users.

This tutorial should help you learn how to correctly detect capabilities, and wherever needed, it will show you how to work out when a browser cannot do what you need, so you can halt your script, or provide an alternative. When there are different ways to do the same thing, and some browsers only support one, while others support another, it will show you how to correctly work out which one to use.

Introduction to JavaScript

JavaScript is a programming language that can be included on web pages to make them more interactive. You can use it to check or modify the contents of forms, change images, open new windows and write dynamic page content. You can even use it with CSS to make DHTML (Dynamic HyperText Markup Language). This allows you to make parts of your web pages appear or disappear or move around on the page. JavaScripts only execute on the page(s) that are on your browser window at any set time. When the user stops viewing that page, any scripts that were running on it are immediately stopped. The only exceptions are cookies or various client side storage APIs, which can be used by many pages to store and pass information between them, even after the pages have been closed.

Before we go any further, let me say; JavaScript has nothing to do with Java. If we are honest, JavaScript, originally nicknamed LiveWire and then LiveScript when it was created by Netscape, should in fact be called ECMAscript as it was renamed when Netscape passed it to the ECMA for standardisation.

JavaScript is a client side, interpreted, object oriented, high level scripting language, while Java is a client side, compiled, object oriented high level language. Now after that mouthful, here's what it means.

Client side
Programs are passed to the computer that the browser is on, and that computer runs them. The alternative is server side, where the program is run on the server and only the results are passed to the computer that the browser is on. Examples of this would be PHP, Perl, ASP, JSP etc.
Interpreted
The program is passed as source code with all the programming language visible. It is then converted into machine code as it is being used. Compiled languages are converted into machine code first then passed around, so you never get to see the original programming language. Java is actually dual half compiled, meaning it is half compiled (to 'byte code') before it is passed, then executed in a virtual machine which converts it to fully compiled code just before use, in order to execute it on the computer's processor. Interpreted languages are generally less fussy about syntax and if you have made mistakes in a part they never use, the mistake usually will not cause you any problems.
Scripting
This is a little harder to define. Scripting languages are often used for performing repetitive tasks. Although they may be complete programming languages, they do not usually go into the depths of complex programs, such as thread and memory management. They may use another program to do the work and simply tell it what to do. They often do not create their own user interfaces, and instead will rely on the other programs to create an interface for them. This is quite accurate for JavaScript. We do not have to tell the browser exactly what to put on the screen for every pixel (though there is a relatively new API known as canvas that makes this possible if needed), we just tell it that we want it to change the document, and it does it. The browser will also take care of the memory management and thread management, leaving JavaScript free to get on with the things it wants to do.
High level
Written in words that are as close to english as possible. The contrast would be with assembly code, where each command can be directly translated into machine code.
Object oriented
See the section on 'object oriented programming' for details.

How is JavaScript constructed

The basic part of a script is a variable, literal or object. A variable is a word that represents a piece of text, a number, a boolean true or false value or an object. A literal is the actual number or piece of text or boolean value that the variable represents. An object is a collection of variables held together by a parent variable, or a document component.

The next most important part of a script is an operator. Operators assign literal values to variables or say what type of tests to perform.

The next most important part of a script is a control structure. Control structures say what scripts should be run if a test is satisfied.

Functions collect control structures, actions and assignments together and can be told to run those pieces of script as and when necessary.

The most obvious parts of a script are the actions it performs. Some of these are done with operators but most are done using methods. Methods are a special kind of function and may do things like submitting forms, writing pages or displaying messages.

Events can be used to detect actions, usually created by the user, such as moving or clicking the mouse, pressing a key or resetting a form. When triggered, events can be used to run functions.

Lastly and not quite so obvious is referencing. This is about working out what to write to access the contents of objects or even the objects themselves.

As an example, think of the following situation. A person clicks a submit button on a form. When they click the button, we want to check if they have filled out their name in a text box and if they have, we want to submit the form. So, we tell the form to detect the submit event. When the event is triggered, we tell it to run the function that holds together the tests and actions. The function contains a control structure that uses a comparison operator to test the text box to see that it is not empty. Of course we have to work out how to reference the text box first. The text box is an object. One of the variables it holds is the text that is written in the text box. The text written in it is a literal. If the text box is not empty, a method is used that submits the form.

Examples

All examples on this page and elsewhere in my JavaScript tutorial are provided free of charge. Please see the terms and conditions page for terms of use.

These examples are all useful in their own right but when used together can produce spectacular results. I will leave that to your own creativity.

Note that the syntax highlighted source of all the header files I use in these examples, as well as several others, is available on my JavaScript libraries page. The header files can also be downloaded from there.

The header file scripts are meant to be real scripts for real use, not tutorials. I have included them on this page as they can be viewed as examples of real programming, not lessons on how to program. I agree that they are not always easy to read, but that is mainly because they are optimised for real use. You may choose to see this as a bad programming method, but I do not, as I can always see what I was intending for the script to do, and it helps to cut down on the transmitted file size, something which modem users are always happy for.

To give you an example of a more 'friendly' format, which is easier to use as a learning aid, one of my readers has kindly produced a reformatted version of my generic re-writable elements script, which is included in the email they sent me.

Non DHTML

The most popular use of JavaScript; changing images when the mouse hangs over a link

The next most popular use of JavaScript; opening new browser windows

The next most popular use of JavaScript; writing dynamic content

The next most popular use of JavaScript; checking forms

Slightly less used but just as useful, retaining variables from one page for use on another page

Some specialist scripts

The most irresponsible script

DHTML

Appearing / disappearing content

Movable content

Rewritable document content

W3C DOM

These scripts any many more are included on my JavaScript libraries page. The page also contains several utility scripts, which you may find useful for your Web development. Please see the terms and conditions page for terms of use.

Games

Ok, so they are far too complicated to be examples, but all of the principles are the same as I have described in these tutorials. These games require various different technologies like HTML, CSS, DHTML, etc. Not all browsers will be able to use all of the games, in particular the 3D games, because they are demanding on resources. The simpler games may even work in archaic browsers like Netscape 3.

These games are all on my games site.

Adding JavaScript to a page

You can add a script anywhere inside the head or body sections of your document. However, to keep your document well structured there are some basic guidelines:

Adding a script to the page

To insert JavaScript into a web page, use the <script> tag. You should use the type attribute to specify the type of script being used, which in the case of JavaScript is text/javascript. It is also possible to the language attribute to say what JavaScript version you are using. In practice, this number means very little to browsers. They may claim to support a specific version, but will have vastly different capabilities. All JavaScript supporting browsers currently in use will support a level of JavaScript equivalent to JavaScript 1.2 (represented as "javascript1.2") or higher, so this is what I will teach you in this tutorial.

Browsers will generally choose an arbitrary version number that they will claim to support, and will run any script that has either no language version number, or a version equal to or lower than the one they claim to support. Since the language is so unreliable, you should generally omit this attribute, although it is common to see scripts using it. Your script can then detect if the browser is capable of running your script, and it can do this a lot more accurately than the version number can.

This is an example of a script tag, used to include a script in your page:

<script type="text/javascript">
//JavaScript goes here
</script>

If your script is incapable of providing its own fallback, but it is needed to access the page, you should include support for non-JavaScript browsers by using:

<noscript>
  <p>This will not be displayed if JavaScript is enabled</p>
</noscript>

Using comments

Comments allow you to leave notes to yourself and other people in your script, and are useful as a reminder of what the script is doing and why. The double slash indicates a single line comment. Everything after it on the same line will be ignored by the script engine. The slash-asterisk indicates a block comment. Everything after it will be ignored by the script engine until an asterisk-slash is encountered.

<script type="text/javascript">

//This is a single line comment

/* This is a
block comment */

</script>

Using external script files

You can also use a separate header file to contain the JavaScript (usually *.js) making it easy to share across pages:

<script src="whatever.js" type="text/javascript"></script>

When using header files, if you put any script code in between the <script ...> and </script> tags, it will only be executed if the browser does not support header files (assuming it does support the version of JavaScript shown in the language attribute, if you used one). In reality, this is only for very old browsers that are not used at all any more, so there is no need for anything to be put there.

Scripts in header files are executed as if they were in the main page. If the script referances any external files, such as images, the addresses it uses are relative to the main page, not the script URI.

Commenting out your scripts

This is not needed any more. All current browsers are aware of script tags, and how to treat their contents, since they have been part of HTML since HTML 3. Browsers that do not understand HTML 3 or scripts (these are virtually never used now) will display the script as if it was the content of the page. You can hide the script from them by commenting out your script with standard HTML comments. Browsers that understand script will simply ignore these comments, and will just interpret the script within them. The opening comment is ignored as if it were a normal single line JavaScript comment. The closing comment will not, so it needs to have a JavaScript comment put before it:

<script type="text/javascript">
<!--

//JavaScript goes here

//-->
</script>

These HTML comments should not be used if you are using an external script file, and are not to be used anywhere else in your scripts.

Dealing with XHTML

Note that when I talk about XHTML, I am talking about pages that are served using a content type of application/xhtml+xml - the majority of pages that use XHTML markup are actually served as text/html and as a result are correctly treated as HTML, not XHTML.

The rules in XHTML are different. Script tags are not treated as being special. Their contents are treated as any other part of XHTML, so various operators can be misinterpreted as part of the markup. To avoid this, it is best to put all scripts in external script files so that they do not interfere with the page itself. If you feel the need to put something directly on the page, you can declare it as CDATA (the default for script contents in normal HTML):

<script type="text/javascript">
<![CDATA[

//JavaScript goes here

]]>
</script>

If you feel the compulsion to comment out your script in XHTML, you must use a more ugly structure to contain your scripts. Again, this really is not needed, since browsers that do not understand script also do not understand XHTML, but in case you want to use it (maybe you are serving it as XHTML to some browsers, and HTML to others), this is what to use:

<script type="text/javascript">
<!--//--><![CDATA[//><!--

//JavaScript goes here

//--><!]]>
</script>

Controlling when the script is activated

By default, any script you write will be processed as the document loads. For example:

<script type="text/javascript">
var myVariable = 'Hello';
window.alert(myVariable);
//as the document loads, a message box saying 'Hello' will be displayed
</script>

Sometimes, this may be undesirable and it may be better to write functions (see the section on Writing functions) and activate them later.

To activate JavaScript, you can use HTML events. Modern browsers can detect a vast number of events on most elements. Older browsers are more limited and can only detect the standard set of events on specific elements. The ones that will work in all browsers I have tested are:

input, textarea, select
onclick, onkeydown, onkeyup, onkeypress, onchange, onfocus, onblur, etc.
form
onsubmit, onreset
a
onclick, onmouseover, onmouseout, onfocus, onblur
body
onload, onunload

Check with the HTML specifications on the W3C site for more details. 'a' elements can also detect onmousemove in all current browsers, but not some of the older browsers that are not used any more. For some of these older browsers, such as Netscape 4, it is possible to compensate using a combination of onmouseover, onmouseout and then document.captureEvents and onmousemove on the body (see the section on Event information). The Event information and more advanced DOM events chapters of this tutorial show how to listen for more event types, and obtain information from them.

These are some examples of using events (shown here using traditional inline event handlers - later on in this tutorial, I will show you alternative ways to attach events to elements that will not require you to alter your HTML):

<a onmouseover="name_of_function(params)" href="somepage.html">
<input onkeypress="myVariable = 2;startTest();">
<select onchange="self.location.href = this.options[this.selectedIndex].value;">
<body onunload="window.alert('bye')">

Another way of making HTML activate JavaScript is to use <a href="javascript:name_of_function(params)"> but in general it is best to use events, so the link can provide an accessible alternative if JavaScript is not available (the event handler can prevent the browser from using the link normally if scripting is available).

In all these cases, the script is contained within HTML attributes, and is subject to the normal rules of HTML entities. If you need to put quotes in the script in between the quotes in the html, use the HTML entity &quot; or &#34; for " style quotes or if your quotes in the HTML are done using the ' quotes (which is unusual ...), you can use &#39; to represent these in the script. If you need to include < or > operators, you should use the &lt; and &gt; entities.

Object oriented programming

Objects

An object is a 'thing'. For example a number is an object. An array is an object. Your browser window is an object. A form in your document is an object. There are hundreds more, and in some cases you can create your own.

Objects can have objects in them. For example, your document can have a form in it. This form is a 'child object' of the document. Therefore the document is the 'parent object' of the form. To reference the child object, we have to go through the parent object, eg. document.myForm

An array can have numbers in its cells. As we have already discussed, the array is an object, and so would be a cell within it, and so is the content of the cell. We cannot refer to the cell itself, but we can refer to its contents: myArray['cell name or number'] for example.

Classes (or types)

A class is a group of objects that are similar in some way. For example, a number and a piece of text can both be stored as a variable (in a way, like the variables you would use in mathematical algebra). In essence, we can say that pieces of text and numbers are examples of class 'variable'.

Numbers can be sub divided into two groups, integers and floats (or doubles). Integers are whole numbers: 1, 2, 3, 4, 0, -1, -2, etc. Floats have decimal points: 1.1, -5.7, 0.5, etc. In this case, we can say that 3 is an instance of class variable, (sub)class number, (sub)class integer.

In fact, a variable is a type of object. All instances of class 'object' have a certain two methods: toString() and valueOf(). Therefore, as 3 is an instance of class object, (sub)class variable, (sub)class number, (sub)class integer, it will inherit the toString() and valueOf() methods provided by the class 'object'.

Classes are not so important in JavaScript as they are in many other object oriented programming languages. Classes can be created when you define your own classes of objects, but it is not usual to create your own 'sub'-classes.

Collections

If you need to know what arrays are, see the section on Variables.

There are many arrays that are inbuilt into each document. The document itself is an array in certain uses. The most obvious of these collections is the images collection. To refer to images in the document, we use
document.images['name of image']
This is a special kind of array, known as a collection.

Properties

Take, for example, an image. When we define images in HTML, we write:

<img src="frog.gif" name="myImage" height="10" width="10" alt="A frog">

The properties of the image would be src, name, height, width, alt and if we also used Style Sheets we might have included several more (like background-color for example). All properties are a type of object so to refer to the src of my image, I would write document.images['myImage'].src

Methods

There are always actions that are associated with objects. For example, a form may be submitted or reset. The actions are methods. To submit a form in non-object-oriented programs, we might write submit('name of form')

That would simply be a function. In object oriented programming like JavaScript, we would use document.nameOfForm.submit()

The reason this is a method and not just a function is that each form will have its own submit() function built in, which is referred to using the object oriented syntax shown above. You will never have to write methods in yourself unless you create your own objects, the browser does that for you.

You can think of it like this:

If wanted, you can run several methods in turn on the same object by using:

referenceToTheObject.method1().method2().method3().method1again()

In this case, method1 must return a class of object that has the method 'method2' etc.

Operators

The most common operators are mathematical operators; +, -, /, * (add, subtract, divide, multiply) for example. Operators can be split into two groups, comparison operators and assignment or 'action' operators. Comparison operators test to see if two variables relate to each other in the specified way, for example, one variable is a higher number than the other. Other operators perform an action on a variable, such as increasing it by one.

The following table gives those that are most commonly used. The JavaScript 1.3 operators such as the identity operator === are supported by all current browsers, but are not used as often as the others. Old browsers that do not understand them will just produce errors.

JavaScript operators
Operator Uses
+adds two numbers or appends two strings - if more than one type of variable is appended, including a string appended to a number or vice-versa, the result will be a string
-subtracts the second number from the first
/divides the first number by the second
*multiplies two numbers
%divide the first number by the second and return the remainder
=assigns the value on the right to the object on the left
+=the object on the left = the object on the left + the value on the right - this also works when appending strings
-=the object on the left = the object on the left - the value on the right
>number on the left must be greater than the number on the right - this also works with strings and values
<number on the left must be less than the number on the right - this also works with strings and values
>=number on the left must be greater than or equal to the number on the right - this also works with strings and values
<=number on the left must be less than or equal to the number on the right - this also works with strings and values
++increment the number
--decrement the number
==the numbers or objects or values must be equal
!=the numbers or objects or values must not be equal
<<bitwise leftshift
>>bitwise rightshift
&bitwise AND
|bitwise OR
^bitwise XOR
~bitwise NOT
!logical NOT (the statement must not be true)
&&logical AND (both statements must be true)
||logical OR (either statement must be true)
inobject or array on the right must have the property or cell on the left
===the numbers or objects or values must be equal, and must be the same variable type
!==the numbers or objects or values must not be equal, or must not be the same variable type

Note, if you do not set the language="javascript1.2" attribute in the <script> tag, 0 == false == '' and undefined == null. If you do set the language attribute to 'javascript1.2', Mozilla/Firefox and other Gecko browsers (but none of the other major browsers) will change this so that none of these will equate to each other. Since the attribute is deprecated anyway, and the JavaScript versions were never standardised, you should not rely on this behaviour. If you need that behaviour, use the === and !== operators.

There are also a few operators that can also be used like functions:

void
void statement or void(statement) (see the section on writing functions)
typeof
typeof variable or typeof(variable) returns the type (or class) of a variable.
eval
eval(string) interprets a string as JavaScript code.

There are also the 'var', 'new' and 'delete' operators. See the section on variables for examples of their uses. Also see the section on the Math object operators below.

Note that JavaScript has no logical XOR operator. If you need that functionality, see my separate XOR article.

Operator precedence

If you ask JavaScript to perform a calculation using multiple operators, those operators will be evaluated in a specific order. For example 3 + 6 * 7 is calculated as ( 6 * 7 ) + 3 because the * is calculated before the +. The order in which these are evaluated is: * / % + - + (where the second + is appending strings). To change the order in which they are calculated, use parenthesis ( ) as the contents of parenthesis are calculated before the contents outside the parenthesis. For example, 3 + 6 * 7 = 45 but ( 3 + 6 ) * 7 = 63

The Math object methods

In reality, these are methods of the Math object but they are used in place of operators.

Math object methods
Operator What it does
Math.abs(n)Returns the absolute value of n
Math.acos(n)Returns (in radians) cos-1 of n
Math.asin(n)Returns (in radians) sin-1 of n
Math.atan(n)Returns (in radians) tan-1 of n
Math.atan2(n,k)Returns the angle (rads) from cartesian coordinates 0,0 to n,k
Math.ceil(n)Returns n rounded up to the nearest whole number
Math.cos(n)Returns cos n (where n is in radians)
Math.exp(n)Returns en
Math.floor(n)Returns n rounded down to the nearest whole number
Math.log(n)Returns ln(n)
Note, to find log10(n), use Math.log(n) / Math.log(10)
Math.max(a,b,c,...)Returns the largest number
Math.min(a,b,c,...)Returns the smallest number
Math.pow(n,k)Returns nk
Math.random()Returns a random number between 0 and 1
Math.round(n)Returns n rounded up or down to the nearest whole number
Math.sin(n)Returns sin n (where n is in radians)
Math.sqrt(n)Returns the square root of n
Math.tan(n)Returns tan n (where n is in radians)

Variables

Variable types are not important in JavaScript. They may be interchanged as necessary. This means that if a variable is a string one minute, it can be an integer the next. The basic variable types are:

character
'a' or '@' or 'k' etc.
string
'hdttgs 8 -46hs () Get the idea'
integer
0 or 1 or 2 or 3 or 4 etc. or -1 or -2 or -3 or -4 etc.
float (or double)
23.546 or -10.46
boolean
true or false
function
A function; see the section on Functions
object
An object; see the section on Object oriented programming
array
A type of object that can store variables in cells (see below)
regular expression
A pattern that can be used to match strings or substrings
undefined
A variable that has not been given a value yet
null
A variable that has been defined but has been assigned the value of null

Integer and float are grouped together as 'number'. Character and string are grouped together as 'string'.

Use the typeof operator to find out which variable type your variable is. Note, typeof will return 'object' for arrays, regular expressions and null in most current browsers. In future, browsers are instead expected to return 'null' for null.

Defining variables

See the section on 'Referencing' subsection 'Avoiding referencing conflicts' to see how to choose names for your variables.

Normal variables

It is good practice to pre-define all variables using the var keyword (this is known as declaring the variables). It is possible to define the variable, leaving its value undefined, or assigning a value immediately, or even to define multiple variables in a single command:

var variablename;
var variablename = value;
var vari1 = value, vari2 = anotherValue, vari3;

JavaScript is fairly lenient, and will create a global variable if you forget to use the 'var' keyword, but you assign a value to an undeclared variable (if you attempt to read the value of an undeclared variable, it will throw an error, as discussed below). However, this can lead to problems if you end up accidentally overwriting variables from another scope (this will be covered in the chapter on Functions). Once a variable is defined do not use 'var variablename' again unless you wish to completely overwrite the variable. It makes no sense to redeclare a variable with the var keyword within the same scope, but browsers will not complain if you do it.

These are some examples of how to define variables, in this case, a string, a number, and a regular expression:

var myFish = 'A fish swam in the river';
var myNumberOfGoats = 23;
var myPattern = /<[^>]*@[^<]*>/gi;

Note that variables can be defined at any point in your script, including inside a control structure that causes that statement never to be executed. However, no matter where you choose to define your variable, the JavaScript engine will create the variable at the start of the current scope. It will hold the value undefined until a statement is executed that gives it a value.

Objects

If you want to create variables that you want to be able to give child properties to, you must define the variable as an object. To define a variable as an object, use either the new Object() or {} syntax:

var variableName = new Object();
var variableName = {myFirstProperty:1,myNextProperty:'hi',etc};

You can then assign child objects or properties to that object as you want:

variableName.aPropertyNameIMadeUp = 'hello';
variableName.aChildObjectNameIMadeUp = new Object();

Note that if you want to run a method on an object that you just created, for true JavaScript 1.2 compliance, you must surround the object declaration with brackets (early Netscape 4 will have problems otherwise). This example will not work in early Netscape 4, and other browsers made at the same time:

new Date().getTime()

This example will work in all JavaScript supporting browsers:

( new Date() ).getTime()

This applies to all object types like Array, Image, Option, Object, Date and ones you create yourself.

Arrays

Arrays are similar to the Object we have just defined, but with some small differences. I like to think of arrays as boxes. Inside the box, there are several compartments or cells. We can vary the number of compartments. To refer to the contents of a compartment, we write the name of the box, then we write square brackets, inside which we put the name or number of the compartment.
To create an array, use either the new Array() or [] syntax:

var nameOfArray = new Array();
var nameOfArray = new Array('content_of_first_cell','and_the_second',8,'blah','etc');
var nameOfArray = ['content_of_first_cell','and_the_second',8,'blah','etc'];

You can also use a single number as a parameter when creating the array object. If the number is a positive integer, an array will be created with that many cells, all of which will be empty: new Array(5). Note that if you set the language version to javascript1.2, some browsers (but not all) will create an array with a single cell containing the number 5. For this reason, it is best not to use a single numeric parameter when creating the array using the new Array(number) syntax.

To refer to the contents of its cells, use the syntax nameOfArray[name_or_number_of_entry] (if using names instead of numbers, the name should be in quotes). Numeric arrays start at 0, not 1, so the first cell is number 0.

In JavaScript, arrays and objects are almost equivalent, and accessing a named cell of an array is the same as accessing a property of an object. However, it is important to note that most of the special array functionality is based on you using numeric arrays - most array methods act only on numeric keys, and will ignore named properties. If you are using arrays with only named properties, then you are almost certainly using them for the wrong reason, and should probably be using objects instead. However, for the purpose of demonstration, if a cell of an array is called 'mycell', you could use either of these to access its contents:

nameOfArray['mycell']
nameOfArray.mycell

There are some important differences between arrays and objects. By default, arrays have far more methods attached to them, for stepping through a numeric array, or splitting it into pieces. In addition, if numbers are used for entries, you can find out how many entries (or variables) the array is holding, using nameOfArray.length

It is possible to create multi-dimensional arrays, by creating aditional arrays as the contents of the cell of another array:

var nameOfArray = new Array(new Array(1,2,3,4),'hello',['a','b','c']);

You can refer to the cells of that array using the square bracket notation twice (or multiple times, depending on how many dimensions your array has):

nameOfArray[name_or_number_of_entry][name_or_number_of_inner_entry]

Other types of objects

Browsers will have many built in types of objects. All JavaScript capable browsers will provide the following aditional object types:

Date
Creates a date object, which can perform calculations based on the date, for example:
var mydate = new Date();
window.alert( 'The year is' + mydate.getFullYear() );
Image
Creates an image that is not visible but is stored in cache. Setting the src attribute of the image causes the image to be loaded and stored in the browser's cache:
var myimage = new Image();
myimage.src = 'thing.gif';
Now each time I want to change an image on the page, I can say document['imagename'].src = myimage.src; and the image will change, without having to wait for the new image to load.
Option
Creates an option that can be added into a select input, for example:
var myoption = new Option('text','value');
selectInput.options[selectInput.options.length] = myoption;

See the section on 'Creating objects' for how to create your own objects that are not one of the pre-defined object types. DOM compliant browsers will provide far more object types - these will be covered in later chapters of this tutorial.

Deleting properties

On several occasions here I have created properties for objects. Even if these are set to undefined or null, they still exist, holding that new value, and will be found using the for-in control structure. You can delete them completely by using the 'delete' keyword:

delete myObject.itsProperty;

Of course, unless you have serious computer memory considerations, or security risks, it is usually not necessary to do this at all, since JavaScript engines clean up after themselves once the variable is no longer referenced.

Avoiding errors with variables

If at any time you refer to a variable that does not exist, you will cause an error. However, JavaScript allows you to refer to one level of undefined child properties of an object (specifically, it allows you to refer to non-existant properties of any variable that is not null or undefined, but it will not allow you to refer to their properties, since an undefined property cannot have properties of its own). This is a very important rule to learn, as it forms the basis of object and capability detection, and is fundamental to making cross browser scripts. The following series of examples will demonstrate what will throw errors:

var myObject = new Object(), nonObject = '', nonObject2;

This sets everything up for the following examples. So far, everything is fine, but in the code below, b is undefined. Not only that, but it has never been declared as existing. This will cause an error when the code attempts to read its value:

var a = b + 5;

In the code below, c has also not been defined or declared. It will not throw an error, however, because the code is assigning a value to it. This will cause the script engine to automatically declare it in the global scope:

c = 5;

In the next example, the parent object has been defined, but its property has not. Since this is only one level of undefined properties, this is allowed, and will not cause an error:

var b = myObject.myChild;

In the next example, the parent object has been defined, but its property has not. Trying to refer to a child of the non existent property means there are now two levels of undefined properties. This will cause an error:

b = myObject.myChild.itsChild;

The actual reason it causes an error is because the code attempts to access the property of an object that does not accept properties (implicitly 'undefined' in this case), and will cause an error if an attempt is made to access them. The same would happen if myObject.myChild was explicitly defined as null or undefined.

In the next example, the parent object has been defined, but it is a type of variable that cannot accept custom child properties, such as a number or string. Trying to create a child property will not throw an error, since it is only one level of undefined properties. However, it will not do anything, since the new property will be rejected:

nonObject.itsChild = 7;
window.alert(nonObject.itsChild);
//This will alert 'undefined'

Null and undefined are special, however, as they cannot have any properties at all, custom or native. Any attempt to access a property of a variable that holds the value null or undefined, will result in an error:

nullVariable = null;
nullVariable.itsChild = 7;
//This will throw an error

In the next example, the parent object has been declared, but it was not given a value. As a result, it holds the value undefined. Trying to refer to a child property means there are now two levels of undefined properties, and will throw an error:

nonObject2.itsChild = 7;

Referencing a variable or just its value

If you assign one variable to another, there are cases where this merely copies the value in one variable and 'pastes' it into the other, but there are also cases where instead of copying the variable, it just provides you with a new way to reference that variable. The following is an example of where this might occur:

var myNewVariable = myOldVariable;

If myOldVariable was already defined as a string, number, boolean, null or undefined, it would simply have copied myOldVariable and 'pasted' it into myNewVariable. If the new variable were changed (for example, using myNewVariable = 'some new value';), myOldVariable retains its old value.

If, on the other hand, myOldVariable was already defined as a function, array, object, regular expression or option (or any other type of object except null), myNewVariable would have been created as a pointer to myOldVariable. The children of myOldVariable would now also be the children of myNewVariable. If the new variable were changed (for example, using myNewVariable = 'some new value';), it would only alter the value of myNewVariable so that it no longer references the same contents as myOldVariable. However, changing the child properties of myNewVariable will change the properties of the object it references, and as a result, myOldVariable would also see the change:

var myOldVariable = new Object();
var myNewVariable = myOldVariable;
myNewVariable.newChild = 'Hello';
alert(myOldVariable.newChild);
//This will alert 'Hello'

More about numbers

JavaScript understands numbers in several formats, allowing you to specify numbers in hex, decimal, and octal. If a 0 precedes a number and there is no number higher than 7, the number is considered to be in octal (base 8) and if the number is preceded by 0x, then it is considered to be in hex (base 16) and can also contain characters A, B, C, D, E, F. Neither may contain decimal points.

With decimal numbers, 12e+4 may be used to replace 12x104 and 12e-4 may be used to replace 12x10-4 etc.

There are a few numbers that are held by the Math object and can be used as variables with the exception that you cannot assign values to them. These are known as constants. There are some constants available that give the document area of the window that is available for writing to. These require specific referencing to obtain and so have been included in the section on referencing items, objects and elements, subsection 'Window size'.

The available Math object constants are:

Math object constants
Math object property Value (approx) Mathematical equivalent
Math.E2.718e
Math.LN20.693ln(2)
Math.LN102.303ln(10)
Math.LOG2E1.442log2(e)
Math.LOG10E0.434log10(e)
Math.PI3.142Pi
Math.SQRT1_20.707(sqrt(2))-1 or sqrt(1/2)
Math.SQRT21.414sqrt(2)

Special string characters

There are a few string characters that can be escaped with a backslash and can be used to replace untypeable characters. These are:

\n
A newline character. Use this if you want to insert a line break into text.
\f
A form feed. Try not to use this ever. Use \n instead.
\r
A carriage return. Try not to use this ever. Use \n instead.
\t
A tab character.
\\
A \ character
\/
A / character (most web developers [myself included on occasion] forget this one - generally, browsers will have no trouble with you forgetting the backslash but it is important for representing closing tags and in theory you should always use the backslash escape when writing a forward slash - see the section on Writing with script for details of where it is needed)

If you need to recognise a Windows linebreak, you need to look for \r\n. If you are trying to add a linebreak (for example, when modifying the value of a textarea input), you should insert a \n character. This will work cross browser. Note that browsers on Windows may convert this \n character into a \r\n automatically, depending on where you use it.

Old Mac Operating Systems (OS 9 and below) used \r for linebreaks, but as far as my tests showed, they are converted to \n by JavaScript. However, just be aware that you may encounter some of these, especially in text inputs (Mac OS 9 is still in use among some Mac users). Unix based Operating Systems (including Mac OS X) use \n for linebreaks.

If you are writing a string that contains double quotes, the easiest way to deal with it is to surround it with single quotes. You should also use double quotes if your string contains single quotes:

var mystring = 'And he said "help" twice';
var mystring = "Help Andy's cat";

You may notice a problem. What if you have both single and double quotes in your string? The solution is to use the escape character '\' to excape the quotes (in fact, you can always escape quotes, even if the string is delimited by the other type of quotes). For example, both of the following are valid:

var mystring = 'And he said "help Andy\'s cat" twice';
var mystring = "And he said \"help Andy's cat\" twice";

If your string becomes too long to fit on one line (this really doesn't matter - you can make the text as long as you want), and you want to split it onto several lines, simply end the string, follow it with the concatenation operator + and start a new string on the next line:

var mystring = 'first line of the string ' +
    'still on the first line of the string - it\'s like we never broke the line';

Note, in theory, you can use \ to break the string:

var mystring = 'like \
  this';

But this has significant problems; the \ will not always cope with trailing whitespace, may or may not correctly treat the leading whitespace before the new line, and does not work correctly in all JavaScript implementations:

I mention this only for completeness. Do not use it. It is easier and better to use the concatenation operator as shown above.

Regular expressions

Certain methods, like the stringObject.replace() and stringObject.match() methods, can recognise patterns in text. To do this, they use regular expressions. These are well documented elsewhere so I will not describe the syntax here. Basically, what you can do is use special characters to recognise text patterns (like \w for characters a-z, A-Z, 0-9 and _). To define a collection of characters as a regular expression, you use the / character (sometimes with a g or i after it). For example, the regular expression /\/\*[\w\W]*\*\// matches block comments.

Some rare browsers do not support regular expressions. In theory, you should be able to detect regular expression support using this:

if( window.RegExp ) { var myRegularExpression = new RegExp("pattern"); }

This should not produce errors in Pocket Internet Explorer and Internet Explorer for Windows CE (as they do not understand the /pattern/options syntax or support the RegExp object). However, as Opera 6- and iCab 2- support the RegExp object, but fail to use it correctly, your scripts will still fail, though hopefully without errors. Thankfully, current releases of Opera and iCab do support regular expressions correctly.

Control structures

The 'if' statement

if( myVariable == 2 ) {
  myVariable = 1;
} else {
  myVariable = 0;
}

If myVariable had been 2 it would now be 1. If it had been anything other than 2 it would now be 0.

'If' statements can also test for the occurence of a child object of an object that may not exist. For example, some browsers provide document.body.style while some older browsers do not even provide document.body. In these browsers, writing 'if( document.body.style )' would just produce an error (see the section on 'Variables' subsection 'Avoiding errors with variables'). In order to solve this problem, we could write this:

if( document.body ) {
  if( document.body.style ) { etc. }
}

However, the && operator has a useful feature that we can use here to combine the two 'if' statements into one:

if( document.body && document.body.style ) { etc. }

The first test would be false, so the browser would not proceed to the second. This is known as a short-circuit. The || operator has a similar feature, but it will only evaluate the second test if the first one fails.

JavaScript understands that if the '{' and '}' (curly brace) characters are left out, then only the next command belongs to that statement:

if( x < 5 )
x++;
window.alert(x);

Here, the alert will always happen reguardless of x, but x will only be incremented if x is less than 5. This may seem convenient, as it allows you to make your code a tiny bit shorter, but I recommend avoiding this syntax. It makes your code harder to read, especially if you start nesting your control structures. It also makes it easy to forget to put them in when you needed them, and also makes debugging code much harder, since you will need to go back through your code to add them so that you can add extra debugging tests. It is best to always use the curly braces, even if they are optional.

As always, there is an exception. Nested 'if' statements like this can start to get difficult to manage:

if( myVariable == 2 ) {
  myVariable = 1;
} else {
  if( myVariable == 5 ) {
    myVariable = 3;
  } else {
    myVariable = 4;
  }
}

By strategically removing curly braces, that can usefully be reduced to this construct (which you may recognise from other programming languages) - note that 'else if' is not written as 'elseif':

if( myVariable == 2 ) {
  myVariable = 1;
} else if( myVariable == 5 ) {
  myVariable = 3;
} else {
  myVariable = 4;
}

The 'for' loop

This is one of the most common constructs in use. Typically, it is used to cycle through the contents of an array, or to create a specific number of new objects, but it can do many more useful things if needed. The syntax of the 'for' loop is as follows:

for( starting_initialise; continue_as_long_as_condition; do_this_each_time )
starting_initialise
This is where you define new variables that you will use in the loop, typically for use with incremental counting. As with all variables, you must declare them (if you have not done so already). You can define multiple variables if needed, using:
var myVariable1 = value, myVariable2 = another_value;
These variables are not restricted to being inside the 'for' loop, and will be available to all code after the loop (in the same scope as the loop).
continue_as_long_as_condition
This is where you define the conditons under which the loop should continue to execute. The syntax is exactly the same as for the 'if' statement, so you can apply more than one continue condition by using the && or || operators:
myVariable1 <= 5 && myVariable2 >= 70;
If the condition is not satisfied when the for loop begins, then it will never loop through it.
do_this_each_time
Once the end of the loop is reached, it will do whatever you tell it to here. Typically, this is used to increment or decrement a stepping variable, and it is possible to perform actions on more than one variable by separating them with a comma:
myVariable1++, myVariable2 -= 4

The following is a full example.

for( var myVariable = 1; myVariable <= 5; myVariable++ ) {
  myArray[myVariable] = 1;
}

myArray[1] to myArray[5] are now 1.

The 'for - in' loop

The 'for - in' loop is used to cycle through all exposed properties of an object (or array). Every time you create properties or methods on an object, these will be added to the list of properties that will be exposed. Most internal properties (the ones that JavaScript creates) will also be exposed, but JavaScript engines are allowed to hide internal properties and methods if they want to. You should not rely on any specific behaviour here, but note that some browsers will give the internal properties and methods of intrinsic objects, and some will not.

Again, you should declare the variable names that you use, if you have not done so already. The syntax of the 'for - in' loop is as follows:

for( var myVariable in anObjectOrArray ) {

This will run through the loop, once for each exposed property in anObjectOrArray. Each time it loops, it assigns the next property name as a string value to myVariable. You can then use array notation to access the value of that property. The following example writes all the exposed properties of the document object:

for( var myVariable in document ) {
  document.write( myVariable + ' = ' + document[myVariable] + '<br>' );
}

Note that if you use this loop on an array, it will list the numbered and named keys, including the internal 'length' property. It is very easy to make mistakes here, so be careful not to mistake these property types for each other.

The 'while' loop

The 'while' loop is identical in behaviour to the 'for' loop, only without the initial setup, and loop-end actions. It will continue to run as long as the condition is satisfied:

var myVariable = 1;

while( myVariable <= 5 ) {
  myArray[myVariable] = 1;
  myVariable++;
}

myArray[1] to myArray[5] are now 1.

Using a feature of the increment (and decrement) operator here, it is possible to shorten the code inside the loop to be just 'myArray[myVariable++] = 1;', and it would have exactly the same effect. Firstly, it would use the value of myVariable to index the array cell, then it would increment myVariable.

This also works in reverse; 'myArray[++myVariable] = 1;'. Firstly, it would increment the value of myVariable, then it would use the new value to index the array cell. If I had done this, myArray[2] to myArray[6] would now be 1.

These features also work outside loops, but this is where you will most commonly see them, so I have included them here.

The 'do - while' loop

This is similar to the while loop, but with an important difference. The condition is evaluated at the end of the loop, meaning that even if the condition is never satisfied, it will still run through the loop at least once.

var myVariable = 1;

do {
  myArray[myVariable] = 1;
  myVariable++;
} while( myVariable <= 5 );

myArray[1] to myArray[5] are now 1.

The 'switch' statement

The 'switch' statement is like repeated 'if' statements, testing a single value to see if it matches one of a set of values:

switch(myVar) {
  case 1:
    //if myVar is 1 this is executed
  case 'sample':
    //if myVar is 'sample' (or 1, see the next paragraph)
    //this is executed
  case false:
   //if myVar is false (or 1 or 'sample', see the next paragraph)
    //this is executed
  default:
    //if myVar does not satisfy any case, (or if it is
    //1 or 'sample' or false, see the next paragraph)
    //this is executed
}

If a case is satisfied, the code beyond that case will also be executed unless the break statement is used. In the above example, if myVar is 1, the code for case 'sample', case false and default will all be executed as well. The solution is to use break; as follows (The use of the break statement is described below).

switch(myVar) {
  case 1:
    //if myVar is 1 this is executed
    break;
  case 'sample':
    //if myVar is 'sample' this is executed
    break;
  case false:
    //if myVar is false this is executed
    break;
  default:
    //if myVar does not satisfy any case, this is executed
	  //break; is unnecessary here as there are no cases following this
}

The 'with' statement

Take for example the following example:

x = Math.round( Math.LN2 + Math.E + Math.pow( y, 4 ) );

Using the 'with' statement, this can be replaced with:

with( Math ) {
  x = round( LN2 + E + pow( y, 4 ) );
}

Note that the 'with' statement brings extra variable names into the current scope. In the example above, if I already had a variable called pow before the 'with' statement, this variable would be unavailable inside the with statement, as it would have been replaced by the method of the Math object (as would any other variables that matched property or method names). Once the 'with' statement is complete, the old variables would become available again.

The quick 'if' statement

This is known as the conditional or ternary operator, and is an easy way to assign different values to a variable, depending on a condition.

var myVariable = document.getElementById ? 1 : 0;

This is identical to:

if( document.getElementById ) {
  var myVariable = 1;
} else {
  var myVariable = 0;
}

The try - catch - finally statement

The 'try - catch - finally' control stucture allows you to detect errors and quietly deal with them without producing error messages or aborting the script, and in fact, without even interrupting the flow of the script that is running. This makes it superior to the original way of handling script errors (without error messages) where scripts are completely aborted:

window.onerror = referenceToFunction;

The syntax of the 'try - catch - finally' control stucture is as follows:

try {
  //do something that might cause an error
} catch( myError ) {
  //if an error occurs, this code will be run
  //two properties will (by default) be available on the
  //object passed to the statement
  alert( myError.name + ': ' + myError.message );
} finally {
  //optional - this code will always be run before the
  //control structure ends, even if you rethrow the error
  //in the catch
}

If an error occurs in the 'try' section, it immediately jumps to the 'catch' part, passing some information about the error. Different browsers provide different information for the same error so don't trust it (in theory, DOM browsers will use a specific set of error types, but this depends on their level of DOM support - Internet Explorer is the least compliant here). Once the 'try' or 'catch' parts have been run, the 'finally' part is run if you have provided one, then the script following the control structure is run, unless you throw another error.

If you nest these statements (one 'try - catch' inside another), you can rethrow the error from the 'catch' part of the inner statement to the 'catch' part of the outer statement (the 'finally' part - if there is one - would still be run before the outer 'catch' is run, but the script following the inner structure will not be run). This is done using the 'throw' method:

try{
  //...some other code goes in here
  try {
    var a = nonexist.b; //this will produce an error
  } catch(myError) {
    //this catches the error and alerts the message
    alert( myError.message );
    //re-throw the error up to the outer try - catch
    throw( myError );
  }
  //...some other code goes in here
} catch( myErrorObject ) {
  //I re-threw the first error, so this is the same error object
  //the message should be the same
  alert( myErrorObject.message );
}

You can also throw your own errors at any point by creating an object with the required properties, and passing it as the parameter when using throw:

try{
  var myEr = new Object();
  myEr.name = 'My error';
  myEr.message = 'You did something I didn\'t like';
  throw( myEr );
} catch( detectedError ) {
  alert( detectedError.name + '\n' + detectedError.message );
}

What is wrong with it?

It's lack of support in older browsers was its only major failing. Thankfully these browsers are hardly used any more. It may have been useful to use this structure detect errors in Netscape 4 (like the 'this' keyword + inline method bug - for example - there are lots more errors), but that browser does not support the statement. It would also be useful for checking for stupid bugs, like where checking for something like navigator.taintEnabled causes errors in older versions of Internet Explorer. However, the error is not correctly thrown for these errors.

Unfortunately, if you use this structure in any script run by a browser that does not support it, the browser will abort the entire script with errors, even if it does not use the part containing the structure. Thankfully, these old browsers can be safely ignored.

It should never be used to detect if a browser supports a method or property like document.getElementById as a proper object detect would suffice.

So when should it be used?

It can be used for W3C DOM scripting, where you may want to avoid DOM mutation errors (for example), which are valid errors, but serve to warn you not to do something, and do not always need to abort the whole script. Older browsers do not support the DOM anyway, so it doesn't matter if they don't understand this part of it. However, they will still run the script (it is not possible to protect them by using the language attribute on the script tag, as you need to use JavaScript 1.2 - not anything higher - to enable Internet Explorer 5 support). This means that the older browsers will still produce errors, unless you define the old error handling method in an earlier script.

It can be used for throwing your own errors if you create the 'error' deliberately under certain circumstances.

It can be used to check if accessing a frameset frame will cause a browser security error (for example, if the page in the frame belongs to another site).

It could also enable you to avoid problems where different browsers support the same methods but expect a different syntax, for example, the selectBox.add method (I did not include this method in my DOM section of the tutorial due to this problem):

try {
  selectBox.add(optionObject,otherOptionObject);
} catch ( e ) {
  selectBox.add(optionObject,index);
}

Conditionals without a condition?

You may notice in the example for "The quick 'if' statement" that I tested for a property without testing for any specific value: 'if( document.getElementById )'

That is valid, and is one of the most useful parts of JavaScript. This is a very important rule to learn, as it forms the basis of object and capability detection, and is fundamental to making cross browser scripts. This will be true if:

document.getElementById != "" &&
document.getElementById != 0 &&
document.getElementById != false &&
document.getElementById != undefined &&
document.getElementById != null

The opposite is also possible: 'if( !document.getElementById )'

This will be true if:

document.getElementById == "" ||
document.getElementById == 0 ||
document.getElementById == false ||
document.getElementById == undefined ||
document.getElementById == null

Using this, you can detect one type of capability, and if it fails, detect another type, and continue until you find one that works.

You can also do this anywhere where a condition is expected, such as with the 'while' loop condition, the 'do - while' loop condition and the 'continue_as_long_as_condition' in the for loop.

Checking for properties with 'in'

The 'in' operator used in the 'for - in' loop has another purpose. It can also be used to check for the existence of named properties of an object. In most cases, it is best to use a conditional without a condition, as shown above. However, there are some cases where you want to test for the existence of a property even thought the property's value may be one that does not evaluate to true. An example would be where you want to check for the existence of a property whose value may be 0, or an empty string, or null.

If you know what the type of the property will be, it is possible to achieve this using identity operators, or the typeof operator, as shown here:

if( typeof( document.body.innerHTML ) == 'string' ) {

However, it is also possible to use the 'in' operator to test for a property. This allows you to test for its existence, no matter what value it currently holds, and no matter what type of value it currently has (even if it has been assigned a value of undefined). In the 'for - in' loop, the 'in' operator returned the name of properties as a string, and so here, it expects the name to be a string. This limits the usefulness a little, as it can only search for the name, and cannot be used to see if one of the properties holds a specific value or value type.

if( 'innerHTML' in document.body ) {

Note that this is around 20 times slower in Internet Explorer than the conditional without a condition, as shown above. In most other browsers, the two alternatives perform about the same. In general, I consider it best to use the more common alternatives, unless you have a specific use that needs the behaviour of the 'in' operator.

Assignments inside a conditional

JavaScript allows you to perform an assignment at the same time as testing if the assignment worked. This can be used inside any conditional, including inside an 'if', 'for', 'while' and 'do - while'.

if( x = document.getElementById('mydiv') ) {...}
do {
  alert( node.tagName );
} while( node = node.parentNode );

Note that Internet Explorer on Mac will produce an error if you try to do this with an array, when it steps off the end of the array.

Continue and break statements and labels

Labels

Labels are used to name the 'while', 'do - while', 'for', 'for - in' and 'switch' control structures. The syntax used is:

LabelName:
Control Structure

Labels are very rarely used in JavaScript.

The break statement

Writing break inside a switch, for, for-in, while or do - while control structure will cause the program to jump to the end of the statement. If you just use, for example:

for( var x = 1; x < 5; x++ ) {
  var y = 1;
  while( y < 7 ) {
    y++;
    if( y == 5 ) { break; }
    document.write(y);
  }
}

The script will jump past the end of the while loop when y is 5. But if you use this:

myForLoop:
for( var x = 1; x < 5; x++ ) {
  var y = 1;
  while( y < 7 ) {
    y++;
    if( y == 5 ) { break myForLoop; }
    document.write(y);
  }
}

The script will jump past the end of the for loop when y is 5.

The continue statement

Writing continue inside a 'for', 'for - in', 'while' or 'do - while' control structure will cause the program to jump to the test condition of the structure and re-evaluate it having performed any 'do_this_each_time' instructions. If you just use this, for example:

for( var x = 1; x < 5; x++ ) {
  var y = 1;
  while( y < 7 ) {
    y++;
    if( y == 5 ) { continue; }
    document.write(y);
  }
}

This script will jump to the test condition of the while loop when y is 5 so 5 will never be written but 6 and 7 will be. If you use this instead:

myForLoop:
for( var x = 1; x < 5; x++ ) {
  var y = 1;
  while( y < 7 ) {
    y++;
    if( y == 5 ) { continue myForLoop; }
    document.write(y);
  }
}

Here, the script will increment x as part of the for loop and then re-evaluate the for condition.

Writing with script

Be careful when writing with script. If script is not available, that content will not be created. You should limit your use of this to parts of the page that are not needed to access page content. If you do write important parts of the page that are unavailable without scripting, you should use <noscript> tags to provide an alternative.

Writing while the page is still loading

If your code is being executed while the page is still loading (in other words, if it is run as part of the initial page layout) put the following:

<script type="text/javascript">
document.write('<p>What ever you want to write<\/p>');
document.writeln('<p>What ever you want to write<\/p>');
//writeln puts a line break after the line.
//This is treated as a line break in the source of HTML
</script>

It will write whatever you put, in whatever part of the page the script is currently running. Of course, you can include HTML tags in there too or some pre-programming.

Note that if you write content using an event handler (such as the onload handler for an image), it will be treated as if the page has completed loading, even if it has not.

Writing after the page has loaded

After the page has completed loading, the rules change. Instead of adding content to the page, it will replace the page. To do this, you should firstly open the document stream (most browsers will automatically do this for you if you just start writing). Then you should write what you want, and finally, you should close the document stream. Again, most browsers will automatically close the stream for you. The notable exception here is the Mozilla/Firefox family of browsers, that will continue to show a loading graphic until you close the stream. Some other browsers may fail to render part or all of the content. Just to be safe, make sure you always close the stream.

<script type="text/javascript">
window.onload = function () {
  document.open();
  document.write('<p>What ever you want to write<\/p>');
  document.write('<p>More stuff you want to write<\/p>');
  document.close();
};
</script>

That will remove everything that is currently being shown and replace it with what you write in there. This is the equivalent of moving the user to a completely new page. You should put <html> tags and things like that in there too if you want to use that method.

You may notice that I close my HTML tags inside the script with a backslash before the forward slash in the closing tag. This is a requirement of the specification (and can cause the HTML validator not to validate your page if you forget it), although all browsers will understand if you omit the backslash.

However, since you can write HTML with script, you can write style or even script tags with it, making one script import another. If you omit the backslash on any </script> tags that you are writing with the script, the browser will read that as the closing tag for the current script, and your script will fail.

The same applies to opening or closing comments (although I fail to see why you would want to write comments using a script). These can be written as '<'+'!--' and '-'+'->'. When the script runs, the plus sign tells it to append the strings, creating a valid HTML comment.

Problems with old browsers

Although not in use any more, you may want to be nice to older browsers like Netscape 4. If using document.write to dynamically write a div element, do not give the div an inline style with left: or top: positioning. This will cause Netscape 4 to fail completely and be unrecoverable without careful use of dialogs and task kills.

Also, Netscape 4 will completely fail to load if you attempt to use this method to create a <div style="position:absolute"> and instead, you should use the unnofficial <layer> tag. You can use if( document.layers ) to detect if the layer tag should be used.

Writing functions

General syntax

Functions group together script code; control structures, operations, method calls, etc. in the same way as a normal script. These functions can then be called when needed, and the code contained within them will be run. This makes it very easy to reuse code without having to repeat it within your script.

Functions are defined using one of these constructs:

Normal function construct
function nameOfFunction(listOfVariableNames) {
  function code should be written here
}
Anonymous function, assigned to a variable
Using this syntax for object methods in early Netscape 4 versions will cause problems with the 'this' keyword due to bugs.
nameOfFunction = function (listOfVariableNames) {
  function code should be written here
};
Normal function construct, assigned to a variable
nameOfFunction = function anotherNameForTheFunction(listOfVariableNames) {
  function code should be written here
};

Note that in this particular case, because the function is being assigned, and not defined normally, the name anotherNameForTheFunction can be used by the code inside the function to refer to the function itself, but the code outside the function cannot see it at all (note that some browsers, mainly Internet Explorer, do not implement this correctly, so you should not rely on it - it is better to use arguments.callee as shown below).

The Function class constructor
functionName = new Function("function code should be written here");

This construct evaluates the code as a string, and is much slower than assigning anonymous functions. It should only be used in places where it is actually needed.

The Function class constructor with parameters
functionName = new Function("varName","varName2","etc.","function code");

See the section on 'Referencing' subsection 'Avoiding referencing conflicts' to see how to choose names for your functions.

Functions are called using one of these:

When created using the normal function construct, the definition does not have to appear at the start of the script (though it is usually best to do so for the sake of clarity). It can even be defined after the the code that calls it. In most cases, no matter where you choose to define your function, the JavaScript engine will create the function at the start of the current scope.

Note that you should never create a function using the normal function construct inside an 'if' statement (or any equivalent control structure):

if( someCondition ) {
  function functionName() {
    ...this will not work in most browsers...
  }
}

This is permitted by Mozilla's JavaScript 1.5, but this conflicts with ECMAScript 3, the core language used by JavaScript 1.5. As a result, Mozilla based browsers allow it, and most others do not (they will always evaluate the function, even if the condition evaluates to false). It is best not to rely on either behaviour, and do not try to declare functions in this way. Declaring functions inside these statements is possible in all current browsers using assigned anonymous functions, and this is the correct way to achieve the desired effect:

var functionName;
if( someCondition ) {
  functionName = function () {
    ...
  };
}

Passing variables to functions

Variables passed to a function are known as arguments.

When a function is called, the variables or values passed to it in the brackets are assigned to the variable names in the brackets of the function definition.

function checkval(passvar) {
  //if I ran the function using the command "checkval('hello')"
  //then passvar would take on the value 'hello'
  if( passvar != "" ) {
    document.myform.mytextinput.value = passvar;
  }
}

This function, when called, will set the value of a text input to whatever value of passvar was, as long as passvar was not blank.

As an example, part of my html will contain this:

<input type="button" onClick="checkval('pygmy')">

When the user clicks on the button, the text input's value will change to 'pygmy'.

You can pass more than one variable to a function using commas to separate the values:

function functionName(variable1,variable2,variable3,etc.) { function code }
functionName(5,6,7,etc.);

If not enough variables are passed to fill up all of the variables in the function declaration, then any remaining variables will contain the value undefined. You can pass no variables to a function like this

function functionName() { function code }
functionName();

If I called that last function using something like this:

functionName(1,2,3,myVar,window,'stringy bit')

The variables would still be passed to the function but I would only be able to access the variables using the arguments collection (which can also be referenced as referenceToFunction.arguments).

You can use the arguments collection to refer to the arguments, even if you did not write the variable name in the function definition, or you can mix it so that some variable names are defined but others are only available using the arguments collection:

function functionName(variable1,variable2) {
  window.alert(variable1); //alerts 5
  window.alert(arguments[0]); //alerts 5
  window.alert(variable2); //alerts 6
  window.alert(arguments[1]); //alerts 6
  window.alert(arguments[2]); //alerts 7
  window.alert(functionName.arguments[3]); //alerts 8
}
functionName(5,6,7,8);

The arguments collection also has a very useful property; arguments.callee. This is a reference to the function itself, meaning that code running inside an anonymous function can still obtain a reference to the function that is being run. This property is not available in some older browsers.

Using the return statement

The return statement causes a function to stop executing at that point. The code that called the function will still continue to execute.

function doWhatever() {
  var apod = Math.pow(3,7);
  return;
  //the following code will not be executed,
  //no matter what
  apod *= 34;
  if( 700 * 3 <= apod ) {
    return;
    //return on its own is more usually
    //used as part of a conditional
  } else {
    window.alert('The script has made a mistake');
  }
}

The following is an example of using the return statement to return a variable from a function:

function appendComment(passvar) {
  //in this case, I have passed a string variable and I return
  //a string variable. I could return any variable type I want.
  passvar += ' without your help';
  return passvar;
}

var myString = appendComment('I did it');
//myString is now 'I did it without your help'

Note that if you need your code to work in older browsers, it is important to make sure that if the return statement is used to return a value, you must ensure that in all cases, the function returns a value, or some Netscape 4 versions will produce errors.

Take, for example, the action of fading. I want to write the same thing repeatedly, slowly fading from one colour to another. Rather than having to calculate this manually, I want to run some script that calculates the fade for me. The script will run nicely as part of a function, where the function returns the next colour at each step, which I can then use. This is useful, because I can then use the same function to produce a variation of the same effect later on, based on different parameters.

function fadeColour( fromcol, tocol, fadePortion ) {
  //in the format fadeColour( 'ff0098', 'fe0934', 0.23 )
  var oF = [], oT = [], oP = [];
  var oH = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
  //get the red, green and blue substrings ...
  for( var x = 0; x < 3; x++ ) {
    //... and convert them from hex into decimal ...
    oF[x] = eval( '0x' + fromcol.substring( 2 * x, ( 2 * x ) + 2 ) );
    oT[x] = eval( '0x' + tocol.substring( 2 * x, ( 2 * x ) + 2 ) );
    //... add on the required portion of difference between the two ...
    oP[x] = Math.round( oF[x] + ( ( oT[x] - oF[x] ) * fadePortion ) );
    //... and convert it back into hex ...
    oP[x] = oH[ ( oP[x] - ( oP[x] % 16 ) ) / 16 ] + oH[ oP[x] % 16 ];
  }
  //... and put them back together again as a colour string
  return '#' + oP.join('');
}

for( var y = 0; y < 10; y++ ) {
  //in 10 steps, fade the colour - also see the section on writing with script
  document.write( '<span style="color:' + fadeColour( 'd2cbff', '000099', y / 9 ) +
  ';">Fade!<\/span> ' );
}

for( var y = 0; y < 12; y++ ) {
  //in 12 steps, fade the colour
  document.write( '<span style="color:' + fadeColour( 'ff0000', '000000', y / 11 ) +
  ';">Fade!<\/span> ' );
}

Warning, if you are returning a value from a function, do not call the function directly from the <a href= method to activate it, or many browsers will (correctly) create a new page containing the returned value as the page content. You can still use return; on its own. You can only return values if you call the function script from another piece of script (like another function).

If you need to call the function using the <a href= method, use the void operator.

Note, returning false for many events will cancel the action. As an example, if a user submits a form, returning false will cause the form not to be submitted. The exception is onmouseover, where returning true will stop the mouseover from having any effect (such as with a link, where returning true will stop the link url from being displayed in the status bar).

Variable scope

Variable scope is one of the very useful features of languages like JavaScript, so even though it may seem a little complicated, it is worth taking the time to learn how it works.

In a basic script, there may be any number of variables and functions. Take the following example:

var a = 1, b = 2, c = 3;
function sample() {
  var d;
  a = 7;
}
sample();
alert(a);

The variables a, b and c are not inside any function when they are declared, so they are in a scope called the global scope. They are available anywhere. Code anywhere else in the script, including inside the sample function, has access to them. The sample function is also global, since it is not inside any other functions. If any other piece of code changes the contents of those variables, then every other part of the code now sees the new contents. As a result, the alert here will show '7', since the value held by the global variable is changed when the function is run.

Variable d is defined inside the sample function, so it is not global. It is in the local scope of the function. What that means is that only the code inside the function can see it. Code outside the function does not even know it exists. This happens with any function. They have the ability to create their own scopes, and their own local variables, without them interfering with variables located in the global scope. Variable names written in the brackets of the function definition are also created as variables in the local scope (and the same applies to the arguments collection):

function sample(myvar) {
  //myvar is now a variable in the local scope
  alert(myvar);
}
sample('hello');

Now try this modification to the earlier code:

var a = 1, b = 2, c = 3;
function sample() {
  var a, d;
  a = 7;
}
sample();
alert(a);

Here, the variable a is redefined inside the function. Because it is declared using the var keyword, it becomes a local instance of a variable. Even though it shares the same name as a global variable, the two are completely independent. The changes made to that variable inside the function only affect the local variable, not the global one. As a result, the alert will show '1', and not '7'.

Now imagine that the code inside the function wants to reference the global variable 'a' instead of the local one. The global scope is special. In JavaScript, the global scope can be referenced using the name 'window'. The code inside the function can use window.a to reference the global 'a' variable. Incidentally, this is why methods like alert can be called using either alert or window.alert, since these methods are globally available (unless they are overwritten in the current scope, of course).

Nested functions

It is possible to create functions inside other functions. Simply declare another function inside the code of an existing function:

var a = 1, b = 2, c = 3;
function sample() {
  var a, d, e;
  function anothersample() {
    var e, f;
  }
  anothersample();
}
sample();

In that example, the anothersample function only exists inside the sample function, and it can only be called by the code inside the sample function. Code outside that function does not even know it exists. The scopes are also nested, so the code inside the anothersample function has access to b and c from the global scope, a and d from the sample scope, and then e and f from its own local scope. It can also use window.a to reference the variable a from the global scope.

Of course, if you assign a reference to the nested function to a global variable, then the function can be accessed globally through that variable. There is not much point in doing that here, but it becomes very useful when creating object methods (these will be covered in detail in a later chapter).

Scopes have memory

Scopes are actually very clever, since they persist over time. Imagine that the code inside a function creates a local variable. It also creates an event handler function that will be triggered when the user clicks a link:

function sample() {
  var a = 20;
  document.links[0].onclick = function () {
    alert(a);
  };
}
sample();

The action that calls the event handler (inner) function happens much later, a long time after the script that created it has finished running. However, the variable 'a' has survived, so the alert will display the number 20.

Using scope to prevent conflicts

Imagine that you are running a script that uses many global variable and function names. You want to put another script on the same page, but there is a chance that the two scripts will use the same variable names as each other, and may conflict. It is easy to workaround this problem by putting the code from each script inside a function, and running the function. That way, the local scope provided by the function will protect the variables overwriting each other in the global scope. Note that when doing this, it is very important that you remember to declare your variables properly.

The easy way to do this without creating a global function is to create an anonymous function, and enclosing it in parenthesis (internally, the engine will then replace that entire construct with the function reference). You can then use the open-close parenthesis to run it immediately. This construct may look a little odd, but it works:

(function () {
  //Put your script code in here
})();

Using nested function scope to preserve instantaneous values

This is an advanced topic that is covered in its own article.

Referencing

How browsers affect it

Referencing is, in my experience, the hardest thing to learn to get right. It is not helped by the fact that the methods required may be different in different browsers and so you have to include several different versions of the code to ensure it will work in all of them. I have put together a list of the most popular browsers available that support JavaScript.

Opera, Mozilla and Safari/Chrome-based browsers currently have the highest level of JavaScript support, but many scripts are poorly written, and are based on the implementation of just a few browsers, typically just Internet Explorer and one other browser. Unfortunately, Internet Explorer 8- does not have a very good JavaScript implementation. It often does not comply with the standards, relying instead on authors to use its non-standard extensions. Many authors do not realise these are non-standard, and end up writing code that relies on these extensions. Other browsers may implement some of these extensions in an attempt to get these scripts to work. To make matters worse, when running in quirks mode, Internet Explorer 9+ removes support for the standards approach, and reverts to the scripting support of earlier versions. Scripts that need to run in both standards and quirks mode will still need to allow for both approaches, even if older versions of Internet Explorer are not being used.

What I have done in this tutorial is to carefully test the responses of as many different browser engines as I could, and put together something that works in as many of them as possible. Where possible, I will rely on the standards compliant version, and only fall back to the alternatives if a standards compliant technique cannot be used.

Although the W3C DOM makes it possible to access any object using a standard DOM structure, the older approaches are still possible, and widely used on the Web. These older approaches will be covered first in this tutorial, as it is necessary to know them when dealing with code that is designed to work in all possible browsers, including the older ones.

Object hierarchy

To reference items (objects) on the page, refer to an object structure or hierarchy. The topmost object is usually 'document' and the next will be different depending on the document structure. Most elements (HTML tags) cannot be referenced in all 4th generation browsers. Those that can are:

In most browsers, these components will be available to scripts immediately after they have been written. If being written by script, they should be available immediately, but in some older browsers they may not be available until that script has completed executing (after the </script> tag). In some more poorly written old browsers, these document components will only be available after the document has loaded.

You can detect when the document has loaded using the window.onload event (which can also be written in HTML as <body onload= ...). Strangely, older Gecko browsers (Mozilla/Firefox 1/Netscape 6+) will need at least one non-entity character to be written between the element's opening tag and the script, even if the element is an image, or the element will not be available to script until after the page loads.

There are a few others elements that can be referenced. For a list of objects, properties, collections and methods, refer to the section on The JavaScript object. In 5th generation browsers (the vast majority of browsers in use today), all elements can be rederenced using the DOM. This will be covered in later sections.

I have written a generic referencing function that can be used to reference all document components that can be referenced (except links), but I suggest you work through the next few sections of the tutorial first instead of taking a shortcut. You might actually understand what I am doing, instead of blindly using my code without knowing how it works.

Avoiding referencing conflicts

Remember that all names in JavaScript are case sensitive. Names must begin with a-z or A-Z or _ and may only contain the characters a-z, A-Z, 0-9 and _. The convention is to use names that describe the object. If the name is made up of more than one word, there should never be a space between the words and it is not usual to have an underscore ( _ ) between the words. The usual practice is to capitalise the first letter of all words except the first.

For example, I have a variable that I will be using to store some text about me. I decide the best name is 'text about me' and I must remove the spaces and capitalise the first letters of all words except the first. Therefore, the variable name becomes 'textAboutMe'.

In order to avoid referencing conflicts, you must make sure that no two JavaScript objects or document elements are given the same name (except inputs - see the next section).

When choosing names for variables, functions and both names and IDs of HTML elements, there is a set of reserved words which you must not use, regardless of the case. These are: abstract, arguments, boolean, break, byte, case, catch, char, class, const, continue, debugger, default, delete, do, double, else, enum, export, extends, false, final, finally, float, for, function, goto, if, implements, import, in, instanceof, int, interface, long, native, new, null, package, private, protected, public, return, short, static, super, switch, synchronized, this, throw, throws, transient, true, try, typeof, var, void, volatile, while, with.

In addition, it is generally a good idea not to use names that conflict with the names of existing properties or methods of the global/window object (unless you really want to overwrite them). See the section on The JavaScript object for more details.

Browser inspecific referencing

Note that to avoid referencing problems, no two elements should ever be given the same name or id. The only exception is form inputs which can share names.

Global references

Variables are referenced simply by typing their name, or by using window.variableName for global variables.

Functions are referenced simply by typing their name, or by using window.functionName for global functions.

'window' (or 'self') may be used to reference the global object for the current document, regardless of what scope your script is running in. If your script is running in another script and you are having trouble referencing global variables because a local variable uses the same name, this problem can be quickly solved by adding 'window.' to the start of the variable name. For example window.myvariable

By a similar nature, you can use 'this' to represent the current object. By default, 'this' will be the window object. If you are writing the script as part of a HTML event handler, 'this' will be the element that detected the event. For example, this can be used to alert the value of the text input:

function myFunction(x) { window.alert(x.value); }
...
<input type="text" onkeypress="myFunction(this)">

Using this same function from a script running in the global scope, the word 'this' refers to the window object. The window object does not have a value property, so the function would alert 'undefined'.

However, if the onkeypress method is activated manually, the word 'this' once again refers to the input, so the function will alert the value of the input:

document.forms[0].elements[0].onkeypress();

Frameset references

There are four very useful references that refer to parts of a frameset. These are 'window' or 'self', 'window.parent', 'window.top' (window is usually omitted) and the window.frames collection.

self and window
These refer to the global object of current web page.
parent
Refers to the window object of the page that is holding the current page in a frameset.
top
Refers to the window object of the page at the top of the frames hierarchy.
window.frames[nameOrNumberOfFrame]
Refers to a frame or iframe held by the current page.

With iframes (if the browser supports them) you have to use the extra word 'window' on the end to reference the window object of the document contained by the iframe (there are alternatives, but this one works in all browsers that support iframes, making it superior to all other techniques, since they only work in a selection of browsers):

window.frames[nameOrNumberOfFrame].window

If the page is the only page being displayed, top, parent, self and window will be equal. If the page is being held within a frameset, self and top will not be equal. If the page is the page containing the frameset, and it itself is not being held within a frameset, self and top will be equal. If someone is loading your page into their frameset and you don't want them to, you can use the self-top relationship to remove your page from their frameset and replace their frameset page with your page using:

if( self != top ) { top.location.replace(self.location.href); }

Note, I could have used this:

if( self != top ) { top.location.href = self.location.href; }

However, that makes the browser add the new entry into its history, so if they clicked their back button, they would be forwarded back to your page again. Unfortunately, Gecko browsers (Mozilla/Firefox/Netscape 6+) will only allow the second option as they have very high security levels relating to cross-site scripts.

Note that using scripts to force pages in and out of framesets is a very bad idea. It causes usability problems for many users, breaks search engines, and annoys your visitors. I know you know how to do it, but that does not mean that you should.

With all frames, the name is set using the name="nameOfFrame" attribute in the <frame ...> tag. The number is automatically generated by the browser in the order that the frames are defined in the HTML, beginning at 0.

To fully reference the window object of another frame when inside a frameset, use one of these:

From there, you can reference items inside that document using document.whatever

parent.frames['otherframename'].document.images['imagename'].src = "sample.gif";

Take, for example, the following frame page structure. The main page - main.html - (which by default has no name, although it can be set with JavaScript, or by opening the page with target="newName") contains two frames; 'left' and 'right'. 'left' contains the page 'links.html'. 'right' contains a page - inner.html - containing a further two frames; 'banner' and 'mainpart'. The locations of 'banner' and 'mainpart' are upper.html and lower.html respectively. The names of the files are irrelevant, but it helps me to explain the structure to you. This produces the following frame tree structure:

       unnamed frame (main.html)
   ____________|____________
  |                         |
left (links.html)         right (inner.html)
                  __________|______________
                 |                         |
              banner (upper.html)      mainpart (lower.html)

The following examples use the frames collection, although any of the above syntaxes would be fine.

If a script were running in main.html, it would be able to reference the window and document objects of lower.html using this:

window.frames['right'].frames['mainpart']
window.frames['right'].frames['mainpart'].document

If a script were running in lower.html, it would be able to reference the window object of links.html using either of these:

window.parent.parent.frames['left']
window.top.frames['left']

Attempting to access the contents of a frame before they have loaded will cause an error. In all browsers, the window object will not exist for that frame unless the page has loaded.

if( !window.frames['nameOfFrame'] ) { window.alert( 'Frame has not loaded' ); }

This algorithm also works with iframes, without requiring any changes.

Forms

Note that this changes in Netscape 4 and other layers browsers if the form is put inside a positioned element. See the section on Element contents for more details. Since these browsers are no longer in use, this behaviour can be safely ignored.

To fully reference a form, use document.formname for a form that was defined with the name="nameOfForm" attribute in the <form ...> tag, or document.forms[number_of_form] either should work. The number is generated automatically by the browser in the order that the forms are defined in the HTML, beginning at 0.

To fully reference an input, use any of these:

reference_to_form.inputname
reference_to_form.elements['inputname']
reference_to_form.elements[number_of_input_(not_image_input)]

Name is defined using the name="nameOfInput" attribute in the <input ...>, <textarea ...>, <button ...> or <select ...> tags. Number is generated automatically by the browser in the order that the inputs are defined in the HTML, beginning at 0.

You can read or write the value of text inputs and textareas with their 'value' property. See the section on The JavaScript object for a full list of what parts of a form can be read or changed.

If more than one input of any kind in the same form share the same name (as is common with radio button inputs), they must be referenced using the numerical array associated with that name. The array will contain an entry for each input with that name, in ascending order of how they appear in the source of the document, beginning at 0. For example, if a radio button has the name 'mybutton', it can be referenced using this:

document.nameOfForm.mybutton

But if two radio buttons share the name 'mybutton', the second button (for example) can be referenced using this:

document.nameOfForm.mybutton[1]

This will be the case for any input type, even if inputs of different types share the same name. In practice, radio buttons will almost always share a name, and checkboxes may occasionally share a name. Even if inputs share a name, they will still have individual entries in the elements collection.

For select boxes, each individual option can be referenced using input_name.options[number_of_option]. For example:

if( document.myForm.mySelect.selectedIndex == 1 ) {
  document.myForm.mySelect.options[3].selected = true;
  if( !document.myForm.myRadio[1].checked ) {
      document.myForm.myRadio[2].checked = false;
    document.myForm.myRadio[1].checked = true;
    document.myForm.myRadio[0].checked = false;
  }
}

You can reference the value of an option tag with '.value' and the text it displays with '.text'. You can also find out how many options there are by using the '.length' property of the options collection.

Note, setting a select option to null will remove it from the select box:

document.forms[number_of_form].mySelect.options[0] = null;

All other options will have their index numbers adjusted to reflect this change. To add or replace one, use this:

document.forms[number_of_form].mySelect.options[number] = new Option('text','value');

Most commonly, number used is the length of the collection, as that will add the new option onto the end:

document.forms[number_of_form].mySelect.options.length

Warning: although the selectInputObject.selectedIndex property should be read-write, it is read-only in Internet Explorer 4. If this causes problems, you car use this:

selectInputObject.options[index].selected = true;

Images

Note that this changes in Netscape 4 and other layers browsers if the image is put inside a positioned element. See the section on Element contents for more details. Since these browsers are no longer in use, this behaviour can be safely ignored.

To reference an image, use any of these:

Name is defined using the name="nameOfImage" attribute in the <image ...> tag. Number is generated automatically by the browser in the order that the images are defined in the HTML, beginning at 0.

Note, if you compare document['imagename'].src to a string constant, you will find that in some browsers it may be the same whereas in others it is not. This is because some browsers will give the image src in the format 'myimage.gif' whereas others may give it in the format 'http://mydomain.co.uk/myimage.gif':

if( document['imagename'].src == "pig.gif" ) {
...
if( document['imagename'].src == myImage.src ) {

A solution to this is to use the string method 'indexOf()'. If the parameter is not contained within the string, the method returns -1, else it returns the position of the first occurrence of the parameter within the string. For example:

if( 'http://mydomain.co.uk/myimage.gif'.indexOf('myimage.gif') +  1 ) {
  //This will be executed
}
if( 'http://mydomain.co.uk/myimage.gif'.indexOf('yourimage.gif') +  1 ) {
  //This will not be executed
}
if( 'http://mydomain.co.uk/myimage.gif'.indexOf('image.gif') +  1 ) {
  //This will be executed. BE WARNED, this may cause a problem
}

Links, anchors and areas

Note that this changes in Netscape 4 and other layers browsers if the link is put inside a positioned element. See the section on Element contents for more details. Since these browsers are no longer in use, this behaviour can be safely ignored.

To reference an <a ...> element, there are two ways (excluding those provided by the DOM). If the name attribute is set, so making the element an anchor (used in <a href="#nameOfLink">) you can refer to it using this:

document.anchors[number_of_<a>_element]

Alternatively, if the href attribute is set, you can refer to it using this:

document.links[number_of_<a>_element]

Number is generated automatically by the browser in the order that the links or anchors are defined in the HTML, beginning at 0.
As <area ...> tags also have the href attribute, they may also be accessed using the links collection, but not the anchors collection. If an <a ...> element has both the the href and name attributes set, it will appear in both collections, although its index may be may not be the same in each. In layers browsers, any links or anchors referred to using the anchors collection will have only the name property available. The collection provides access to the anchors purely for the purpose of changing the name, used when setting the location with location.hash.

Browser specific referencing

The fact that this referencing is browser specific does not mean that it will not work in all browsers. What it does mean is that you will have to include more than one way of referencing items to ensure that you manage to reference them correctly. There are four ways to reference items:

The main one is the W3C DOM, which is supported by all current browsers. Later on in this tutorial, I will move over to concentrating only on the DOM version, since it is much more capable than the others. However, since this stage of the tutorial deals with the tasks required for 4th generation DHTML, I will use all of these techniques, so the code will work in older browsers as well. When you write your own code, some of this may be unnecessary, depending on what browsers people use to view your site, but you will frequently encounter code that uses techniques, so it helps to understand what they are doing, and why.

There are many similarities and crossovers between the different methods and in many cases there are only two different variations required at a time, for example when finding the size of the screen or the button of the mouse that was clicked (see the sections 'Window size and scrolling' and 'Event information').

Writing script using browser specific referencing is not difficult. The main thing to remember is never to make assumptions. Just because a browser uses Netscape compliant syntax to reference an element, that does not mean that is uses Netscape compliant syntax to reference the style.

The way I will show you to write never makes assumptions. What is more, it uses all four types of syntax (where they exist) allowing browsers that support any one of the types of referencing to work. That is the only way to produce true cross-browser scripts.

To download the browsers themselves, see my list of '4th+ generation' browsers.

Element position

As an example of browser specific script, I will demonstrate how to find the position of an element on a web page. Note, that in older browsers, this can be a little unreliable with elements that are in another element that is positioned absolutely or relatively using CSS. To be nice to Netscape 4, I will demonstrate this with a link, but it could work anywhere in other browsers.

In Netscape compatible browsers, every link object has two properties that give its current position, linkObject.x and linkObject.y. In DOM compatible browsers, these do not exist. Instead, the elementObject.offsetLeft and elementObject.offsetTop properties are given. These give the position of the link relative to an arbitrary ancestor node, known as the offsetParent. Different DOM browsers give a different offsetParent, some may say the paragraph that the link is in, while some may say the container the paragraph is in. This can all get a bit complicated.

Fortunately, with DOM browsers, the offsetParent will also have the offsetTop and offsetLeft properties, giving its position relative to its offsetParent, and so the cycle continues, until the offsetParent is the topmost node in the document, which will always be at offset (0,0). We can access the offsetParent using elementOrCurrentNode.offsetParent. So all we have to do is add the offsets together and we will end up with the same answer as we would if we had the linkObject.x and linkObject.y properties.

There is one exception to this; if an element has position:fixed, its offsetParent should be the viewport, which cannot be referenced, so the offsetParent is null instead. However, its offsetTop and offsetLeft still hold real values. (The situation is actually the same for the HTML or BODY element when they are attached to the viewport, but they generally have no offset there, so it does not matter.) What this means is that even when the offsetParent is null, you still need to remember to add on the last set of offsetTop and offsetLeft values. Opera 9+, Internet Explorer, and ICEBrowser get this right. Other browsers will treat the offsetParent of a fixed position element as either the HTML or BODY elements, but this is harmless because they themselves have no offsets, so the algorithm will always work.

The following function returns an array containing [leftPosition,topPosition] for an element.

function findPosition( oElement ) {
  if( typeof( oElement.offsetParent ) != 'undefined' ) {
    for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
      posX += oElement.offsetLeft;
      posY += oElement.offsetTop;
    }
    return [ posX, posY ];
  } else {
    return [ oElement.x, oElement.y ];
  }
}

Test it here: get the position of this link.

With links, you can also check what the text of the link is:

var theText = linkObject.text ? linkObject.text : linkObject.innerText;

Element position with scrolling offsets

The simple findPosition script calculates the position of elements anywhere on the page, but only if they are not inside a scrollable element. This serves the vast majority of purposes, and is the preferred approach in most cases. Inside a scrollable element (created using the overflow style), the element can scroll the contents, but the calculated position will assume the element is not scrolled at all. To work out the position after scrolling, the script needs to step through all parent elements (or positioned containers if at least one element in the offsetParent chain is positioned absolutely), and subtract any scrolling offsets for them.

Working out which scrollable elements are actually going to affect the position requires knowledge of what elements are containers for any positioned elements in the chain. This can be complicated to calculate for a script, so the easiest approach is to make sure that every element with an overflow of anything other than visible also has a position style set to something other than the default static. This way, they will all appear in the offsetParent chain, and can be easily subtracted in the same loop that adds the offsetLeft and offsetTop.

As a separate complication, there is the document scrolling. The document scrollbar can be produced by either the BODY or HTML elements, depending on the browser, DOCTYPE, and overflow styles on the elements. Different browsers also reflect this scrolling in different ways when checking for scrolling on these elements, and this makes it completely unreliable. The easiest approach is simply to exclude any scrolling on these elements. If needed, it can be added later by working out how far the document has been scrolled (covered in the window size and scrolling chapter).

The scrolling offset for each element is available as the scrollTop and scrollLeft properties for those elements. The following code performs as described (including making sure it does not subtract any scrolling for the target element itself). Make sure all scrollable elements have the position style set as shown above.

function findPositionWithScrolling( oElement ) {
  if( typeof( oElement.offsetParent ) != 'undefined' ) {
    var originalElement = oElement;
    for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
      posX += oElement.offsetLeft;
      posY += oElement.offsetTop;
      if( oElement != originalElement && oElement != document.body && oElement != document.documentElement ) {
        posX -= oElement.scrollLeft;
        posY -= oElement.scrollTop;
      }
    }
    return [ posX, posY ];
  } else {
    return [ oElement.x, oElement.y ];
  }
}

Note that this performs much more slowly than the simple findPosition script, so it should only be used where absolutely necessary.

It is possible to use DOM to step up the entire parentNode chain and subtract the scrolling offsets for elements from the value returned by the simple findPosition script. If the computed position style is fixed it can stop. If it is absolute it can stop including scrolling offsets until it reaches another element whose computed position style is not static (done by jumping to the element's offsetParent). This would then work without needing to set the position style on all scrollable elements. However, asking a browser to perform this many computations will cause the script to run very slowly, so should be avoided. I do not recommend using this code, but it is here if you still feel you need it:

function findPositionWithScrolling( oElement ) {
  function getNextAncestor( oElement ) {
    var actualStyle;
    if( window.getComputedStyle ) {
      actualStyle = getComputedStyle(oElement,null).position;
    } else if( oElement.currentStyle ) {
      actualStyle = oElement.currentStyle.position;
    } else {
      //fallback for browsers with low support - only reliable for inline styles
      actualStyle = oElement.style.position;
    }
    if( actualStyle == 'absolute' || actualStyle == 'fixed' ) {
      //the offsetParent of a fixed position element is null so it will stop
      return oElement.offsetParent;
    }
    return oElement.parentNode;
  }
  if( typeof( oElement.offsetParent ) != 'undefined' ) {
    var originalElement = oElement;
    for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
      posX += oElement.offsetLeft;
      posY += oElement.offsetTop;
    }
    if( !originalElement.parentNode || !originalElement.style || typeof( originalElement.scrollTop ) == 'undefined' ) {
      //older browsers cannot check element scrolling
      return [ posX, posY ];
    }
    oElement = getNextAncestor(originalElement);
    while( oElement && oElement != document.body && oElement != document.documentElement ) {
      posX -= oElement.scrollLeft;
      posY -= oElement.scrollTop;
      oElement = getNextAncestor(oElement);
    }
    return [ posX, posY ];
  } else {
    return [ oElement.x, oElement.y ];
  }
}

How the various DOM parts of this work will be covered in later chapters of this tutorial.

DHTML

One of the most popular uses of JavaScript is DHTML (Dynamic HyperText Markup Language). Strictly speaking, DHTML is using JavaScript to modify the CSS styles of HTML elements. All current browsers support much more than I will describe at this stage of the tutorial. For now, I will concentrate on the capabilities introduced by 4th generation browsers. Old browser versions (such as Netscape 4 and Opera 6-) limit the abilities of '4th generation' DHTML, as these can only handle basic DHTML. However, they still provide easily enough flexibility to make features like pop-up menus, message scrollers, mouse trails or falling snow effects.

This requires parts of the page to move or appear/disappear. In current browsers, this can be done with any element, and in older browsers can be done reliably using the div element and in many cases the span element, when they are positioned using the position: absolute; or position: relative; style (except when changing the display style). Personally, I almost always use the div element and I can guarantee that it will work with everything I show you here. Some very old browsers (particularly Netscape 4) have problems using the span element when positioned absolutely.

Once you have set up the HTML (using a few workarounds for bugs), you can reference the positioned element. You can then use that reference to change the visibility, position, background colour, z-index, clipping and size of the positioned element. You can even rewrite the contents of the positioned element or create new positioned elements after the page has loaded. DHTML also allows you to change the background colour of the whole document and change the display style of ANY element.

Sadly, DHTML is always browser specific, in other words different browsers require different commands (though with current browsers supporting the DOM, it is often possible to use only that approach in current Web sites). Using the techniques I will show you, this is not difficult to work with, to make sure that no matter what syntax the browser expects, the script can check what to use, and use it correctly. Many authors make the mistake of detecting browsers and assuming capabilities, or detecting one capability and assuming another. This tutorial does not rely on these mistaken assumptions. At no point should you need to detect specific browsers.

4th generation browsers

There are several browsers that I know of that can handle DHTML (often called 'version 4' or '4th generation' browsers, even though their version number may not actually be as high or low as 4). The following list gives a different line for each significantly different browser. Some of these browser engines are used in a large number of other browsers. For example:

Major browsers

Internet Explorer 5+ (Win)
Uses document.getElementById and document.all
Mozilla Gecko (FireFox, Netscape 6+)
Uses document.getElementById (also secretly uses document.all)
Opera 7+
Uses document.getElementById and document.all (document.all is secretly supported in Opera 9.5+)
KDE KHTML/WebKit (Safari, Chrome, Konqueror 3+, OmniWeb 4.5+)
Uses document.getElementById (Chrome/Safari 3 also secretly uses document.all)

Minor browsers

These browsers are not used very much, or are no longer the current versions of these browsers. Some may have significant bugs, limited capabilities, or limited event detection.

Several of these use the Mozilla Organization's Java based Rhino JavaScript engine to power their browsers. The Rhino JavaScript engine only powers the JavaScript language interpreter, it does not govern how JavaScript interacts with the web page. Each browser will have its own DHTML (or DOM) implementation.

(Internet) Explorer 5 (Mac)
Uses document.getElementById and document.all
iCab 3
Uses document.getElementById and document.all
Internet Explorer 4 (Win)
Uses document.all
(Internet) Explorer 4 (Mac)
Uses document.all
Opera 5-6
Uses document.getElementById
Mozilla Rhino engine (ICEbrowser)
Uses document.getElementById and document.all
Mozilla Rhino engine (Escape/Evo 5)
Uses document.getElementById and document.all
Mozilla Rhino engine (Escape 4)
Uses document.layers
Mozilla Rhino engine (Clue browser)
Uses document.getElementById and document.all
Tkhtml+EES (Tkhtml Hv3)
Uses document.getElementById
Netscape 4
Uses document.layers
OmniWeb 4.2-
Uses document.layers (also optionally supports document.all)
iCab 2
Uses document.getElementById and document.all
Konqueror 2
Uses document.getElementById and document.all

Television or mobile device browsers

Due to the limitations of the devices that these browsers run on, most will have limited event detection. Several popular browsers engines are also used in mobile/TV browsers, including Opera, WebKit and Gecko. Others use their own engines, and are given below:

NetFront
Uses document.getElementById
Pocket Internet Explorer
Uses document.all and document.getElementById in newer versions
Netgem 4 browser (NetBox)
Uses document.getElementById
OpenTV
Uses document.getElementById
iPanel MicroBrowser with advanced modules
Uses document.getElementById
WebTV (MSN TV) Plus and extended Classic
Uses document.all (and non-nested layers alternative)

More information

For more details of these browsers, and to download them, please see my list of '4th+ generation' browsers.

(You can also try this site for a fairly complete list.)

While many JavaScript and DHTML functions worked in Konqueror 2, the browser was unable to detect and handle events properly so most DHTML applications did not work. Konqueror 3 is a big improvement and is now a highly capable browser, so I have given compatibility notes for Konqueror 3+ only.

I am unsure of the abilities of NetBox, iPanel MicroBrowser and OpenTV as I do not have them to test. The NetBox site claims partial CSS 2, so I assume CSS positioning, and W3C DOM 1, so I assume DHTML. OpenTV claims absolute CSS positioning and DOM DHTML. The EIS claims that one of their advanced modules for iPanel MicroBrowser can handle DOM. They might not support innerHTML because that is not part of the DOM standard, even though it has been widely accepted by almost all DOM browsers.

Note that even though Escape 4 is a layers browser, like Netscape 4, it supports both the Netscape syntax and the style object for changing element style, with the exception of clipping. WebTV 2.8+ also supports both syntaxes, but is a proprietary DOM browser.

Setting up the HTML

There are two types of positioning that are of interest here. These are absolute and relative. Absolute allows you to position an element anywhere you want to, in relation to the page. Relative positions the element where it normally would be, and offsets it by whatever amount you specify.

Most DHTML is done with absolutely positioned elements. Relatively positioned elements do not accept the 'clip' style and do not allow their clipping to be changed. The useful feature of relatively positioned elements is that they become containers for absolutely positioned elements. So if you put an absolutely positioned element inside a relatively positioned element, its position will be in relation to the relatively positioned element, and not the whole page.

You can create an absolutely positioned elements using this:

<div id="mydiv" style="position: absolute; left: 10px; top: 120px;">contents</div>

And you can create an relatively positioned elements using this:

<div id="mydiv" style="position: relative; left: 10px; top: 120px;">contents</div>

The ID is important, because that is what will be used to reference the positioned element. Layers browsers call these positioned elements 'layers'.

Bugs / workarounds

If using document.write to create the positioned element, Netscape 4 will fail to load the page. Netscape 4 is not important any more, so I suggest you ignore it. If you really need to support it, you can use the unofficial <layer ...> tag instead of an absolutely positioned div, and the unofficial <ilayer ...> tag instead of of a relatively positioned div. Use if( document.layers ) to find out if you need to.

Similarly in Netscape 4, setting any styles using inline style attributes on elements inside a positioned element will cause the page not to load. Instead, create a class for the elements and style them in the main stylesheet.

Referencing the positioned element

To reference a positioned element, there are several ways. The official one is a standard called the W3C DOM. It is supported well enough by all current browsers. There are also two alternatives for the few old browsers that may still be in use. One of these techniques has two different versions.

DOM

DOM browsers use the document.getElementById method to reference the element. You can use a simple if statement to check for the existence of this method. In all current browsers, this will be supported, so in the majority of current scripts, you can simply assume it is supported, and ignore the other approaches.

if( document.getElementById ){
	var myReference = document.getElementById('divID');
}

Proprietary DOM

Proprietary DOM browsers use the document.all collection to reference the element. Some browsers support both the DOM and proprietary DOM for referencing elements.

if( document.all ){
	var myReference = document.all['divID'];
}

Layers

Layers browsers use the document.layers collection to reference the element. Layers in the layers collection can be accessed using either their ID or their numerical index. They will also be able to reference the element through document.divID (where divID is the id that you gave the div). Netscape 4 was the main layers browser, and was the first browser capable of performing DHTML. However, the layers approach has been abandoned by current browsers.

if( document.layers ){
	var myReference = document.layers['divID'];
}

Layers also have one behaviour that sets them apart from the other approached, and can make them difficult to write for. If a positioned element is inside another positioned element, the inner one does not appear inside the layers collection of the document. Instead, it appears inside the layers collection for the document object of the outer layer. This means that that to reference any layer by its ID, you must recursively step through every layers collection in the document, until you find the right one. Since ID attributes must be unique within a document anyway, this approach is needlessly convoluted, which is one of the reasons why it was abandoned.

Combining the approaches

The three techniques can be combined into a single function, that will use the DOM method if it is available. Failing that, it will use the proprietary DOM, and failing that, layers. If it fails to find it in the document's layers collection, it steps through the entries in the collection, and checks the layers collection of each of those, and continues recursively.

function getRefToDiv(divID,oDoc) {
  if( document.getElementById ) {
    return document.getElementById(divID); }
  if( document.all ) {
    return document.all[divID]; }
  if( !oDoc ) { oDoc = document; }
  if( document.layers ) {
    if( oDoc.layers[divID] ) { return oDoc.layers[divID]; } else {
      //repeatedly run through all child layers
      for( var x = 0, y; !y && x < oDoc.layers.length; x++ ) {
        //on success, return that layer, else return nothing
        y = getRefToDiv(divID,oDoc.layers[x].document); }
    return y; } }
  return false;
}

Changing the visibility

Changing the visibility style allows you to make a positioned element appear and disappear. It can be set to either 'visible' or 'hidden'. Layers browsers use 'show' and 'hide'. There are some layers browsers that support both versions. The code here will detect what is supported and use it, instead of detecting one thing and assuming something else.

Once you have referenced the positioned element, you will need to reference its style. In non-layers browsers, you will have to add '.style' to the end of the reference. Layers browsers treat the styles as direct properties of the element itself. To work out which one to use, simply check for the existence of the style property. You can then set the relevant value:

So, after all of that, a full set of instructions to make a positioned element visible would be:

function showDiv(divID_as_a_string) {
  //get a reference as above ...
  myReference = getRefToDiv(divID_as_a_string);
  if( !myReference ) {
    window.alert('Nothing works in this browser');
    return; //don't go any further
  }
  //now we have a reference to it
  if( myReference.style ) {
    //DOM & proprietary DOM
    myReference.style.visibility = 'visible';
  } else {
    //layers syntax
    myReference.visibility = 'show';
  }
}

showDiv('myDiv');

...   ...

<div style="position:absolute;left:0px;top:0px;" id="myDiv">
    contents
</div>

Test it here: show the hidden element.

This positioned element was hidden. Hide it again.

Of course, don't actually alert the message 'Nothing works in this browser', instead, offer them an alternative that does not require the fancy script.

Changing the position

You can adjust the position by using left or top the same way as we used visibility above, and then specifying the position in pixels. (Note, this top is not the same as the window.top mentioned in the section on frameset references.) In order to retrieve the left and top properties as I am about to do, the left and top styles must have been set using the inline style syntax, or by being initially set with JavaScript.

When a strict doctype is used, many browsers require you to use the correct units (20px, not 20), but many older browsers do not understand this, such as Opera 5 ('top' only), layers browsers, WebTV etc. There is no direct way to detect if a browser understands the units or not, since it may accept the values, even if it does not support them. For this reason, I detect document.childNodes, as this is one of the basic requirements for proper DOM support. DOM style requires units to be used, and therefore all browsers that understand DOM also understand the units.

In this example, left and top are both initially set to 10px using an inline style attribute. The code will check if it needs to use a '.style' reference, and if so, it will change the reference the style object instead, allowing the style to be changed for all versions in just one step. It will then retrieve the current positions, add on the new amount and set that as the new position. When it retrieves the current value, it uses parseInt, because the value may contains the units, depending on the browser.

<div style="position:absolute;left:10px;top:10px;" ...

... ...

var noPx = document.childNodes ? 'px' : 0;
if( myReference.style ) { myReference = myReference.style; }
myReference.left = ( parseInt(myReference.left) + 10 ) + noPx;
myReference.top = ( parseInt(myReference.top) + 20 ) + noPx;

The div will move 10 pixels down, and 20 pixels to the right. It will now be at coords - top: 30px; left: 20 px;

Test it here: move this element.

This can quite easily be used along with a timer which fires every few milliseconds and repositions a positioned element according to the scrolling offset to make the element appear static on the screen (position:fixed; is preferred, however).

Changing the background colour

It would be nice to change the text colour as well, but layers browsers cannot do that. To change the background colour, there are three ways. Layers browsers use bgColor. DOM and proprietary DOM browsers can use background and backgroundColor. Opera 5.x can only change the background style, all the others (including Opera 6+) can change both. 'myReference' must be obtained as above. Note; some versions of Opera 5 will only change the background colour if it has already been explicitly defined, and is not inherited.

Warning, Opera 7-7.1 will return the string 'Warning' if you check for bgColor - so if you are checking for which one to change, you must put that syntax last.

if( myReference.style ) { myReference = myReference.style; }
if( myReference.background ) {
  //supported by most browsers
  //like Gecko browsers and the IE series
  myReference.background = '#00ff00';
} else if( myReference.backgroundColor ) {
  //supported by most browsers
  myReference.backgroundColor = '#00ff00';
} else if( myReference.bgColor ) {
  //used by layers browsers
  myReference.bgColor = '#00ff00';
} else {
  //FAILURE, there is no way to change the background colour
}

As setting the wrong one will not cause any problems, this can be easily simplified to:

if( myReference.style ) { myReference = myReference.style; }
myReference.bgColor = '#00ff00';
myReference.background = '#00ff00';
myReference.backgroundColor = '#00ff00';

Changing the z-index

The z-index of positioned elements defines what order they should be stacked above each other. The z-index should be a positive integer. The higher the z-index, the more positioned elements it will be stacked on top of. Two elements must not be given the same z-index. To read the z-index of a positioned element, it must be already defined using the inline style syntax.

if( myReference.style ) { myReference = myReference.style; }
myReference.zIndex = 100;

Test it here: Click the link in each positioned element to raise its z-index above the z-index of the other element.

Changing the clipping

Clipping can only be used on absolutely positioned elements.

This technique is usually used for message scrollers. In browsers that do not support clipping, it is possible to provide them with an iframe so that the contents of that can be scrolled. Please see my scroller example for a more detailed explaination of what clipping is and how it works.

You may want to check that the element can be clipped (there is no real need to, as browsers that do not understand it will just ignore you). This is very difficult as browsers like Opera 6- will give 'rect()' if asked for the clip style but have no way of using it. I have found no good way to do this. The best I can do is to check if myReference.innerHTML and myReference.clip are supported. I do not know of any browsers that should have problems with this as they support innerHTML but not clipping, but do give 'rect()' if asked for the clip style. However, setting the clipping in browsers that do not support it will not cause errors, it just has no effect. As Escape supports the style object but does not support the style.clip syntax, it is important to make sure it uses the clip object instead, so this is put first.

if( myReference.clip ) {
  myReference.clip.left = 0;
  myReference.clip.top = 0;
  myReference.clip.right = 10;
  myReference.clip.bottom = 10;
} else if( myReference.style ) {
  //top right bottom left
  myReference.style.clip = 'rect(0px,10px,10px,0px)';
} else {
  //FAILURE, nothing works
}

Test it here: select a clipping rectangle for the positioned element below. The background shows through wherever the clipping hides parts of the element:

Line one of the positioned element.                 Your browser does not support clipping.
Line two of the positioned element.

Background Background Background
Background Background Background

Note that although the CSS 2.1 standard requires browsers to support rect() with commas between parameters, Internet Explorer 6 and 7 do not apply the rule if it contains commas, if the page's DOCTYPE triggers "standards rendering mode". (This is due to a mistake in the CSS 2.0 specification.) Note that this only applies when the style is set with a stylesheet; if set with a script, it will work with the correct syntax. To avoid this problem, simply set the style twice in the stylesheet, once with commas, and once without. Browsers will use whichever version they understand.

Changing the size

Layers browsers expect you to use the resizeTo method, while a few browsers expect you to change the pixelWidth and pixelHeight properties of the style object. All other browsers expect you to change the height and width properties of the syle object. You can set these last two anyway, reguardless of what the browser exects, because the browser will just ignore them. Some browsers will not re-arrange the contents of the element to suit the new size. In layers browsers, this is equivalent to setting clip.bottom and clip.right, which are the properties that should be read to get the current size. Although this is not accurate as the layer could extend beyond either of these clips, it is the best that layers browsers provide. To change only width or height in layers browsers, you must change the right or bottom properties of the clip object.

Some browsers will not shrink the element smaller than its contents. Some will clip the contents to the new size. Some will rearrange the contents to fit the new size. Some will shrink the element but leave the contents as they were, still visible.

As with element position, some older browsers do not like to have the units (px) written after the size, while newer browsers require it for strict doctypes. Again, a quick detect sorts the good browsers from the not-so-good.

if( myReference.style ) { myReference = myReference.style; }
if( myReference.resizeTo ) {
  myReference.resizeTo( newWidth, newHeight );
}
var noPx = document.childNodes ? 'px' : 0;
myReference.width = newWidth + noPx;
myReference.pixelWidth = newWidth;
myReference.height = newHeight + noPx;
myReference.pixelHeight = newHeight;

Try it here: select a new size for the element:

Test
Test

Rewriting the contents

This functionality has been completely replaced in the W3C DOM, using various methods. The old DHTML approach should not be used in any new Web pages, and is best removed and replaced with DOM functionality (or innerHTML, which has now been standardised, and supported by all current browsers). It is retained here to allow it to be recognised.

In the following example, myReference is obtained as before. Most browsers provide the innerHTML property, which you can rewrite with the new HTML. Layers browsers allow you to use the methods open, write and close on the document object of the layer. Some browsers (like Opera) will also provide the document property of the positioned element, but that is a reference back to the main document. Rewriting that will cause no end of problems so we must check that this is not the case first.

if( typeof( myReference.innerHTML ) != 'undefined' ) {
  //used by all current browsers
  myReference.innerHTML = 'some <b>new</b> content';
} else if( myReference.document && myReference.document != window.document ) {
  //used by layers browsers
  myReference.document.open();
  myReference.document.write('some <b>new</b> content');
  myReference.document.close();
}

It is also possible to extend support to Opera 5 and 6 if needed, by providing an iframe (which should be given a name attribute), checking if it has loaded, then rewriting its contents:

if( typeof( myReference.innerHTML ) != 'undefined' ) {
  //used by all current browsers
  myReference.innerHTML = 'some <b>new</b> content';
} else if( myReference.document && myReference.document != window.document ) {
  //used by layers browsers
  myReference.document.open();
  myReference.document.write('some <b>new</b> content');
  myReference.document.close();
} else if( window.frames && window.frames.length && window.frames['nameOfIframe'] ) {
  //used by browsers like Opera 6-
  myReference = window.frames['nameOfIframe'].window;
  myReference.document.open();
  myReference.document.write('some <b>new</b> content');
  myReference.document.close();
}

Test it here: rewrite the contents of the positioned element below (the Opera 5-6 workaround is not included).

This is the positioned element. The contents have not yet been rewritten.

Creating new positioned elements

This functionality has been completely replaced in the W3C DOM, using createElement. The old DHTML approach should not be used in any new Web pages, and is best removed and replaced with DOM functionality. It is retained here to allow it to be recognised.

As the page is loading, we can create new positioned elements using document.write, or just regular HTML. Once the page has loaded, 5th generation browsers allow us to create any new elements, but some 4th generation browsers also allow us to create elements after the page has loaded.

Most browsers allow the innerHTML property of an element to be manipulated (the Internet Explorer series also provide insertAdjacentHTML, but most other DOM browsers do not include it). Layers browsers (with the exception of Omniweb 4.2-) provide a constructor that creates new empty layers, whose contents can then be written. Before trying to use this constructor, we must check it exists. Although OmniWeb 4.2- provides the Layer constructor, using it causes errors, so I use document.classes to isolate OmniWeb 4.2- and protect it from the script.

With the layers syntax, the new layer is entered into the layers collections using its numerical index but is not entered into the collection with a name, so we can add it there ourselves if we want to. We also have to specify a nominal maximum width. It will be positioned differently in different layers browsers, so we have to position it exactly ourselves. It will be hidden, so we must show it. With the innerHTML syntax, we can create ANY new content, but to be cross browser, I will create a positioned element.

Opera 7-7.1 incorrectly strips all existing tags in the parent element of their style attributes when reading innerHTML, producing an odd effect. To combat this, I use insertAdjacentHTML if the browser provides it, as this does not produce problems.

This first example will create a new positioned element as a child of the document.

if( document.layers && window.Layer && document.classes ) {
  //create a layer 350px wide
  document.layers['newName'] = new Layer( 350 );
  //write its content
  document.layers['newName'].document.open();
  document.layers['newName'].document.write('new content');
  document.layers['newName'].document.close();
  //style it
  document.layers['newName'].left = 0;
  document.layers['newName'].top = 0;
  document.layers['newName'].visibility = 'show';
} else if( document.body ) {
  var theString = '<div style="position:absolute;left:0px;top:0px;' +
    'width:350px;">new content</div>';
  if( document.body.insertAdjacentHTML ) {
    document.body.insertAdjacentHTML( 'beforeEnd', theString );
  } else if( typeof( document.body.innerHTML ) != 'undefined' ) {
    document.body.innerHTML += theString;
  } else {
    //FAILURE, nothing works
  }
} else {
  //FAILURE, nothing works
}

Th next example will create a new positioned element as a child of an existing positioned element (myReference is obtained as above):

if( document.layers && window.Layer && document.classes ) {
  //create a layer 350px wide
  document.layers['newName'] = new Layer( 350, myReference );
  //write its content
  document.layers['newName'].document.open();
  document.layers['newName'].document.write('new content');
  document.layers['newName'].document.close();
  //style it
  document.layers['newName'].left = 0;
  document.layers['newName'].top = 0;
  document.layers['newName'].visibility = 'show';
} else {
  var theString = '<div style="position:absolute;left:0px;top:0px;' +
    'width:350px;">new content</div>';
  if( myReference.insertAdjacentHTML ) {
    myReference.insertAdjacentHTML( 'beforeEnd', theString );
  } else if( typeof( myReference.innerHTML ) != 'undefined' ) {
    myReference.innerHTML += theString;
  } else {
    //FAILURE, nothing works
  }
}

Test it here: create a new element (new elements will alternate in colour and will be slightly offset from each other to make it more easy to see what is happening).

This is the original content of the existing positioned element. It will not be changed.

Changing the background colour of the whole document

There are three ways to change the background colour. Old browsers use the bgColor property to style the background of the entire document. In current browsers, the HTML element and the BODY element are rendered separately, by default with the HTML element taking up just a few pixels outside the BODY. They are able to be styled separately, so to change the document background, both of them need to be changed. The HTML element is referenced in these browsers as document.documentElement, and the BODY is referenced as document.body.

Setting the wrong one will not cause errors (as long as they exist), so I set all of them at the same time.

if( document.documentElement && document.documentElement.style ) {
    document.documentElement.style.backgroundColor = 'red'; }
if( document.body && document.body.style ) {
    document.body.style.backgroundColor = 'red'; }
document.bgColor = 'red';

Test it here: change the background colour of this document to:

Changing the display style of any element

All current browsers support the display style, and can be told to display any element like an inline element (display:inline;), like a block element (display:block;), or not to display it at all(display:none;). Most also support many other display styles. However, some older browsers do not allow the display style to be changed with JavaScript, so if the display style is initially set to 'none' and then you attempt to set it to 'block' or '', the display style will not actually change, and the content will remain invisible. This is also the case for browsers where JavaScript is disabled.

For this reason, it is best to change the display style with script after the page has loaded. Browsers that do not understand it will just ignore you, and allow the user to view the content. It may not be as neat as being able to show and hide it, but at least they will be able to see it.

The display style is different from the visibility style. Setting the visibility style to 'hidden' will hide the element from view, but will leave the space for it. Setting the display style to 'none' will remove it from view and will re-arrange the contents of the page to fill the hole. This can make the feature very useful for menu systems, as with very simple programming, the browser can take care of all the re-arranging to make the page look nice again.

Browsers that allow elements to have their display style changed will all use document.getElementById or document.all, not document.layers. This makes life slightly easier. Of course, it is nice to be able to tell users that this will not work in their browser, so we will want to detect if the browser allows us to change the display style first. To do this, we must initially set the display style using inline style sheets, with it set the the element's default display ('inline' for elements like spans, and 'block' for elements such as paragraphs or DIV).

The HTML

In this example, I will have a span inside a paragraph:

<p>This is a paragraph.
<span style="display:inline;" id="aUniqueId">This is a span
within the paragraph whose display style has been set to
'inline' using an inline style sheet.</span> This is another
sentence in the paragraph, but not in the span.<p>

The JavaScript

function changeDisplay( elementId, setTo ) {
  var theElement;
  if( document.getElementById ) {
    //DOM
    theElement = document.getElementById( elementId );
  } else if( document.all ) {
    //Proprietary DOM
    theElement = document.all[ elementId ];
  }
  if( !theElement ) {
    /* The page has not loaded, or the browser claims to
    support document.getElementById or document.all but
    cannot actually use either */
    return;
  }
  //Reference the style ...
  if( theElement.style ) { theElement = theElement.style; }
  if( typeof( theElement.display ) == 'undefined' ) {
    //The browser does not allow us to change the display style
    //Alert something sensible (not what I have here ...)
    window.alert( 'Your browser does not support this' );
    return;
  }
  //Change the display style
  theElement.display = setTo;
}

Try it here: select a display style for the span in the paragraph below:

This is a paragraph. This is a span within the paragraph whose display style has been set to 'inline' using an inline style sheet. This is another sentence in the paragraph, but not in the span.

Element contents

The DOM provides an easy way to reference all elements, no matter where they are in a document. However, it is possible to reference many elements in a way that works in DOM browsers, and older browsers. This can be useful if you need your scripts to work in browsers like Netscape 4, even though they are not used any more.

Layers browsers like Netscape 4 are different to all other browsers. They give each positioned element a new document object. All forms, images, links and child layers inside that layer, must be referenced through this new document object. So instead of document.formname, it becomes myReference.document.formname, etc.

How to reference anything in a generic way

MacroMedia first came up with a generic reference-all function for use in their 'wysiwyg' page editors. It could reference images and forms and all types of layers. This would look for any object with a name or id that matched the required name. Inspired by that, I have written my own (completely separately) that could also reference a few other things. It is a little larger as a result.

The only thing it cannot reference is a link object, because due to restrictions in older browsers, link objects are only accessible using their numerical index. Also, unlike anchor objects, the name attribute is not accessible with link objects, even if they have one. If referred to using the anchors collection, the link properties are not available, although the name attribute is. By checking every anchor to see if its name matches the requested name, I can reference the correct anchor. However, I cannot reference the link.

Note that if the name refers to inputs in a form that share the same name, this function will return the collection of inputs sharing that name.

function MWJ_findObj( oName, oFrame, oDoc ) {
  /* if not working on a layer, document should be set to the
  document of the working frame
  if the working frame is not set, use the window object
  of the current document
  WARNING: - cross frame scripting will cause errors if
  your page is in a frameset from a different domain */
  if( !oDoc ) { if( oFrame ) { oDoc = oFrame.document; } else { oDoc = window.document; } }

  //check for images, forms, layers
  if( oDoc[oName] ) { return oDoc[oName]; }

  //check for pDOM layers
  if( oDoc.all && oDoc.all[oName] ) { return oDoc.all[oName]; }

  //check for DOM layers
  if( oDoc.getElementById && oDoc.getElementById(oName) ) {
    return oDoc.getElementById(oName); }

  //check for form elements
  for( var x = 0; x < oDoc.forms.length; x++ ) {
    if( oDoc.forms[x][oName] ) { return oDoc.forms[x][oName]; } }

  //check for anchor elements
  //NOTE: only anchor properties will be available,
  //NOT link properties (in layers browsers)
  for( var x = 0; x < oDoc.anchors.length; x++ ) {
    if( oDoc.anchors[x].name == oName ) {
      return oDoc.anchors[x]; } }

  //check for any of the above within a layer in layers browsers
  for( var x = 0; document.layers && x < oDoc.layers.length; x++ ) {
    var theOb = MWJ_findObj( oName, null, oDoc.layers[x].document );
      if( theOb ) { return theOb; } }

  //check for frames, variables or functions
  if( !oFrame && window[oName] ) { return window[oName]; }
  if( oFrame && oFrame[oName] ) { return oFrame[oName]; }

  //if checking through frames, check for any of the above within
  //each child frame
  for( var x = 0; oFrame && oFrame.frames && x < oFrame.frames.length; x++ ) {
    var theOb = MWJ_findObj( oName, oFrame.frames[x], oFrame.frames[x].document ); if( theOb ) { return theOb; } }

  return null;
}

without comments, this becomes:

function MWJ_findObj( oName, oFrame, oDoc ) {
  if( !oDoc ) { if( oFrame ) { oDoc = oFrame.document; } else { oDoc = window.document; } }
  if( oDoc[oName] ) { return oDoc[oName]; } if( oDoc.all && oDoc.all[oName] ) { return oDoc.all[oName]; }
  if( oDoc.getElementById && oDoc.getElementById(oName) ) { return oDoc.getElementById(oName); }
  for( var x = 0; x < oDoc.forms.length; x++ ) { if( oDoc.forms[x][oName] ) { return oDoc.forms[x][oName]; } }
  for( var x = 0; x < oDoc.anchors.length; x++ ) { if( oDoc.anchors[x].name == oName ) { return oDoc.anchors[x]; } }
  for( var x = 0; document.layers && x < oDoc.layers.length; x++ ) {
    var theOb = MWJ_findObj( oName, null, oDoc.layers[x].document ); if( theOb ) { return theOb; } }
  if( !oFrame && window[oName] ) { return window[oName]; } if( oFrame && oFrame[oName] ) { return oFrame[oName]; }
  for( var x = 0; oFrame && oFrame.frames && x < oFrame.frames.length; x++ ) {
    var theOb = MWJ_findObj( oName, oFrame.frames[x], oFrame.frames[x].document ); if( theOb ) { return theOb; } }
  return null;
}

This function can be called in two ways; to reference an item in the current document:

var theOb = MWJ_findObj( NameOrId );

To reference an item in any other document in a frameset, you can choose how high in the frame structure to start. Remember that if using 'top', someone else may load your page in their frameset, and this will produce an error.

var theOb = MWJ_findObj( NameOrId, ReferenceToTopMostFrameToSearch );

Window size and scrolling

Finding the size of the browser window

There are some constants available that give the document area of the window that is available for writing to. These will not be available until after the document has loaded and the method used for referencing them is browser specific. The available constants are:

window.innerHeight/Width
Provided by most browsers, but not Internet Explorer 8-, and even in Internet Explorer 9+, it is not available in quirks mode.
document.body.clientHeight/Width
Provided by many browsers, including Internet Explorer.
document.documentElement.­clientHeight/Width
Provided by most DOM browsers, including Internet Explorer.

This is a little messy because the clientHeight/Width properties can mean different things in different browsers, and even different things in the same browser, depending on whether the document type declaration triggers the browser's strict mode or quirks mode. In some cases, they refer to the dimensions of the window, and sometimes they refer to the dimensions of the contents of the document. The table below shows what the properties mean in different browsers, and different modes:

Properties and what they relate to
Browser window.
innerHeight
document.
body.
clientHeight
document.
documentElement.
clientHeight
Opera 9.5+ strictwindowdocumentwindow
Opera 9.5+ quirkswindowwindowdocument
Opera 7-9.2windowwindowdocument
Opera 6windowwindowN/A
Mozilla strictwindowdocumentwindow
Mozilla quirkswindowwindowdocument
Newer KHTMLwindowdocumentwindow
Older KHTMLwindowdocumentdocument
Chrome/Safari 4+windowdocumentwindow
Safari 3-windowdocumentdocument
iCab 3windowdocumentdocument
iCab 2windowwindowN/A
IE 9+ strictwindowdocumentwindow
IE 6-8 strictN/Adocumentwindow
IE 5+ quirksN/Awindow0
IE 4N/AwindowN/A
ICEbrowserwindowwindowdocument
Tkhtml Hv3windowwindowdocument
Netscape 4windowN/AN/A

As you can see, the browsers seem to have settled on at least one reliable property; innerHeight. Internet Explorer took a long time to make up its mind, and its influence means that other browsers change their clientHeight behaviour in different versions in order to match it. For now, almost all browsers provide window.innerHeight/Width so that can be used. The other values may swap the values around when the browsers are in strict/quirks mode. Fortunately, the only browser that does not provide innerHeight, Internet Explorer 8- (and 9+ in quirks mode), gives 0 in quirks mode for the root element's clientHeight. This means that if we see a value on the documentElement's properties, and the browser does not provide the properties on the window object, we can assume it is Internet Explorer 6-8 in strict mode.

The most accurate method I could come up with uses the following algorithm:

  1. If window.innerHeight/Width is provided, that is fully trustworthy, use that (Hooray!).
  2. Else if document.documentElement.clientHeight/Width is provided and either one is greater than 0, use that.
  3. Else use document.body.clientHeight/Width.

This algorithm will fail with IE 6-8 in 'standards compliant mode' and all versions in quirks mode if the window is shrunk to 0px by 0px. This is only possible when the user actively shrinks the window to hide the page within it. Even then, it will just give the height of the document instead, so it should not be a problem. The following code performs the algorithm as described.

function alertSize() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  window.alert( 'Width = ' + myWidth );
  window.alert( 'Height = ' + myHeight );
}

Test it here: get the inner dimensions of this window.

Note that browsers may take the width either inside or outside the scrollbar (if there is one). In most current browsers, you can ensure that your page triggers standards mode, then use documentElement.clientHeight to get the height inside any scrollbar. window.innerHeight can be used to get the height outside any scrollbar, but that is not possible in Internet Explorer 8-. It is a little messy, and if you do need to make the distinction, you will need to decide which browsers are likely to be important. If you need to reliably get the dimensions inside the scrollbar, including in all current browsers (not IE 6-), you can create a fixed position element with top, right, bottom and left all set to 0, and measure its offsetHeight and offsetWidth.

Finding how far the window has been scrolled

When trying to produce document effects like falling snow, or more serious things like menus that remain in the same place relative to the browser window when the user scrolls, it is essential to be able to obtain the scrolling offsets in both the horizontal and vertical direction.

There are also three ways to find this out, but it is easier than finding the size of the window. Most browsers provide window.pageXOffset/pageYOffset. These are completely reliable. Once again, Internet Explorer 8- (and 9+ in quirks mode) is the odd one out, as it does not provide these properties. Internet Explorer and some other browsers will provide document.body.scrollLeft/Top. In strict mode, IE 6+ and a few other browsers, provide document.documentElement.scrollLeft/Top.

If the scrollLeft/Top properties are provided on either the document.body or document.documentElement, they are reliable in everything except older Safari and OmniWeb 4.5+, which return -8 if the scrolling is 0, but get all other scrolling offsets correct. However, as they correctly provide window.pageXOffset/pageYOffset, this script will not have any problems.

The following script will obtain the scrolling offsets.

function getScrollXY() {
  var scrOfX = 0, scrOfY = 0;
  if( typeof( window.pageYOffset ) == 'number' ) {
    //Netscape compliant
    scrOfY = window.pageYOffset;
    scrOfX = window.pageXOffset;
  } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
    //DOM compliant
    scrOfY = document.body.scrollTop;
    scrOfX = document.body.scrollLeft;
  } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
    //IE6 standards compliant mode
    scrOfY = document.documentElement.scrollTop;
    scrOfX = document.documentElement.scrollLeft;
  }
  return [ scrOfX, scrOfY ];
}

Test it here: get the scrolling offsets.

Event information

As with DHTML, this is always browser specific. When an event is triggered, the browser keeps some information about the event that we can use. There are two ways that browsers use to allow us to access this information. DOM compatible browsers (including Internet Explorer 9+ in standards mode) pass the event information to the event handler function as the first argument. Some older browsers also use this version.

Internet Explorer and a few other browsers store the event information in an event register, which we can access by writing 'window.event'. Internet Explorer versions that support the DOM approach disable their support for it in quirks mode, so even if older IE versions do not need to be catered for, scripts that need to run in quirks mode will still need to allow the IE approach. This tutorial covers both approaches, and will work no matter which IE version is used, and no matter which mode the document is rendered in. For practical cases, I recommend continuing to use this approach.

We also have to tell the browser what objects should detect what events and we also have to tell it what to do when that object detects that event.

Although it is no longer used, you may want the script to work in Netscape 4, since it is capable of doing this. In practical use, I recommend against adding Netscape 4 support, and include the Netscape 4 workaround here so that you can learn to recognise it, and possibly remove it from older scripts. With positioned elements and the document itself, Netscape 4 and Escape 4 will need to be told to capture the events first. Some other browsers (such as Mozilla/Firefox/Netscape 6+) may also provide the methods, but they do not actually do anything. Some others provide them but do not know how to use them, so you should check for the specific Event type as well. For example the simplest code used to listen for a keyup event would just be this:

document.onkeyup = alertkey;
//where alertKey is a function that will handle the event

Code designed to work with Netscape 4 will use this instead:

//Only Netscape 4 and Escape 4 need this first line
if( document.captureEvents && Event.KEYUP ) { document.captureEvents( Event.KEYUP ); }
document.onkeyup = alertkey;
//where alertKey is a function that will handle the event

Problems can arise if one element detects an event where a parent element also detects it. For example, if the document is told to detect when the user presses a key, and a text box in the document is also told to detect when the user presses a key, when the user presses a key in the text box, should the document react or the text box? Or both? And in what order? Some browsers will use capturing to say which element(s) should detect the event and in what order, while some will use bubbling, and many will do neither. I will not describe either of these here as they are far beyond the scope of this stage of the tutorial. I will cover them later in the DOM events part of the tutorial.

For a full list of events that elements can detect cross-browser, see the section on The JavaScript object' subsection 'Standard document components. To see what events can be captured using captureEvents, see the same section, subsection 'window.Event. To see what information is passed about events, see the same section, sub section 'Event objects.

The following examples attempt to solve as many problems as possible.

Detecting the keyup event over the page and extracting the key code.

Note that browsers may give different key code numbers for keypad keys. Also, many browsers do not give key code numbers for control keys (like F1, delete, backspace, alt, arrow keys etc.). Netscape 4 gives a different key code for the letter 'a' to all other browsers. It may be more useful to use String.fromCharCode(key_code) which converts the key code back to its relevant key (like 'G', for example).

//first, tell the browsers to react to the event
if( document.captureEvents && Event.KEYUP ) {
  //remove this part if you do not need Netscape 4 to work
  document.captureEvents( Event.KEYUP );
}
/* this next line tells the browser to detect a keyup
event over the whole document and when it detects it,
it should run the event handler function 'alertkey' */
document.onkeyup = alertkey;

//now create the event handler function to process the event
function alertkey(e) {
  if( !e ) {
    //if the browser did not pass the event information to the
    //function, we will have to obtain it from the event register
    if( window.event ) {
      //Internet Explorer 8-
      e = window.event;
    } else {
      //total failure, we have no way of referencing the event
      return;
    }
  }
  if( typeof( e.keyCode ) == 'number'  ) {
    //DOM
    e = e.keyCode;
  } else if( typeof( e.which ) == 'number' ) {
    //NS 4 compatible, including many older browsers
    e = e.which;
  } else if( typeof( e.charCode ) == 'number'  ) {
    //also NS 6+, Mozilla 0.9+
    e = e.charCode;
  } else {
    //total failure, we have no way of obtaining the key code
    return;
  }
  window.alert('The key pressed has keycode ' + e +
    ' and is key ' + String.fromCharCode( e ) );
}

Test it here: Click this link to start / stop keyup detection then press any key to test this script. Note that some keys give VERY odd responses with String.fromCharCode. The only reliable ones are letter and number keys on the main part of the keyboard.

Detecting the mouse coordinates when it moves over a positioned element

There are three ways that are reliably supported which give mouse coordinates. There is also one unreliable way. All are given as properties of the event object, such as eventObject.clientX. These will be used to obtain the coordinates relative to the entire page.

With clientX/Y, the standard was not very well written, so some older browsers made mistakes when implementing it. The coordinates should be relative to the displayed portion of the page, but Opera 6-, Konqueror 2- and iCab 2- (none of which are the current versions of those browsers) give the coordinates relative to the entire page. There is no easy way to detect if a browser supports it correctly and the only way to write this piece of script is to detect the browsers that do not comply with the standard and provide appropriate scripts. This is the only time I will tell you to do this.

Clue browser also makes this mistake, but as it cannot detect scrolling, there is no need to compensate.

Note that Opera 7+, Konqueror 3+ and iCab 3+ actually comply with the standard, but as they also provides pageX, the script I will show you uses that instead, so again, the problem is avoided.

Opera 6- can be detected because the property 'navigator.userAgent' contains the string 'Opera', even if it is running in IE5 emulation mode. iCab 2- can be detected because its window.ScriptEngine method contains the string 'InScript', even if it is running in emulation mode. Konqueror 2 can be detected because the property 'navigator.vendor' is 'KDE', even if it is running in emulation mode.

  1. If pageX/Y is supplied, pageX/Y is relative to the whole page, and is completely reliable.
  2. If clientX/Y is supplied, clientX/Y should be relative to displayed portion of page (DOM compatible).
  3. Sometimes clientX/Y is relative to the whole page (in browsers that did not implement the specification properly).

Virtually all current browsers provide both pageX/Y and clientX/Y. Internet Explorer 8- (and 9+ in quirks mode) is the only current browser that provides clentX/Y, but not pageX/Y.

See the last section, 'Window size and scrolling', for information on how to detect how far the page has been scrolled. See the section on 'DHTML', for how to reference the positioned element.

if( myReference.captureEvents && Event.MOUSEMOVE ) {
  //remove this part if you do not need Netscape 4 to work
  myReference.captureEvents( Event.MOUSEMOVE );
}
myReference.onmousemove = alertCoord;

function alertCoord(e) {
  if( !e ) {
    if( window.event ) {
      //Internet Explorer 8-
      e = window.event;
    } else {
      //total failure, we have no way of referencing the event
      return;
    }
  }
  if( typeof( e.pageX ) == 'number' ) {
    //most browsers
    var xcoord = e.pageX;
    var ycoord = e.pageY;
  } else if( typeof( e.clientX ) == 'number' ) {
    //Internet Explorer 8- and older browsers
    //other browsers provide this, but follow the pageX/Y branch
    var xcoord = e.clientX;
    var ycoord = e.clientY;
    var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
     ( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
     ( navigator.vendor == 'KDE' );
    if( !badOldBrowser ) {
      if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
        //IE 4, 5 & 6 (in non-standards compliant mode)
        xcoord += document.body.scrollLeft;
        ycoord += document.body.scrollTop;
      } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
        //IE 6 (in standards compliant mode)
        xcoord += document.documentElement.scrollLeft;
        ycoord += document.documentElement.scrollTop;
      }
    }
  } else {
    //total failure, we have no way of obtaining the mouse coordinates
    return;
  }
  window.alert('Mouse coordinates are ('+xcoord+','+ycoord+')');
}

Test it here: pass your mouse over this link to obtain its coordinates.

To detect mouse coordinates over the whole document, use document instead of myReference.

Since all the problematic old browser versions have now been replaced with versions that work correctly, you may want to remove the sniffer, and keep the code clean. You may not want to do this if there is a chance that any of your visitors are using those older versions (they won't be). If that is not a problem for you, you could use this code instead:

document.onmousemove = alertCoord;

function alertCoord(e) {
  var xcoord, ycoord;
  if( !e ) { e = window.event; }
  if( !e ) { return; }
  if( typeof( e.pageX ) == 'number' ) {
    xcoord = e.pageX;
    ycoord = e.pageY;
  } else if( typeof( e.clientX ) == 'number' ) {
    xcoord = e.clientX;
    ycoord = e.clientY;
    if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
      xcoord += document.body.scrollLeft;
      ycoord += document.body.scrollTop;
    } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
      xcoord += document.documentElement.scrollLeft;
      ycoord += document.documentElement.scrollTop;
    }
  } else { return; }
  window.alert('Mouse coordinates are ('+xcoord+','+ycoord+')');
}

Detecting mouse buttons

This example gives two methods for detecting then handling events with an link element. One is written using the standard HTML syntax and one of which is written using JavaScript syntax. It will extract the mouse button that triggered the event. Note: this may not work with 'click' events.

There is also the oncontextmenu event that fires in most current browsers when a user activates their context menu, but that is not the same thing, since it also covers Ctrl+Click on Macs, and the context menu key on Windows and Linux/UNIX.

The mouse button is passed using either the which or button properties. With 'which', 1 is left button, 2 is middle button, 3 is right button. With button, the standard says that 0 is left button, 1 is middle button, 2 is right button, but in IE compatible browsers, 1 is left button, 4 is middle button, 2 is right button. Escape/Evo 5 uses a totally different numbering system, 1 is left button, 2 is middle button, 0 is right button - I suggest you ignore Escape/Evo.

The only time you should detect a button is when you want, for example, to create a drag-drop type effect, and you want to ensure they are using the left (normal dragging) button. You should never abuse it to break the user's context menu, as that will only make your site inaccessible, and annoying for your users, and is not reliable anyway. While it is possible in many browsers to detect and replace the user's context menu (for example, to provide a DHTML menu with options relating to the item that the user clicked), you should avoid applying this to the entire document, as many users use their context menu for navigation, and will have problems using your page if you prevent them from opening the browser's context menu.

The event registration attribute (onmouseup="etc.") is equivalent to an event handler function, and has access to all the usual function information, such as the attributes collection. It can get a bit messy writing all of the button detection code in there, so I want to pass it to the main handler function. Remember that in DOM compatible browsers, the first argument is the event, so I need to pass that too. I also want the handler to have access to the element that triggered the event so I must pass that.

<script type="text/javascript">

//link and form elements do not need to be told to capture.
//Using the JavaScript syntax, we have to wait for the relevant
//part of the page to load before telling it to detect the event
//This is easiest done with a load event listener
window.onload = function () { document.links[0].onmousedown=alertBut; }

function alertBut( e, evElement ) {
  if( !e ) {
    if( window.event ) {
      //Internet Explorer 8-
      e = window.event;
    } else {
      //total failure, we have no way of referencing the event
      return;
    }
  }
  if( typeof( e.which ) == 'number' ) {
    //Netscape compatible
    e = e.which;
  } else if( typeof( e.button ) == 'number' ) {
    //DOM
    e = e.button;
  } else {
    //total failure, we have no way of obtaining the button
    return;
  }
  if( !evElement ) { evElement = this; }
  /* 'this' will exist if I have used object.onEventName = alertBut;
  If I have passed evElement from the onmouseup attribute,
  'this' will refer to window */
  window.alert( evElement + ' was clicked with button ' + e );
}

</script>

<a onmouseup="alertBut(arguments[0],this);" href="whatever">

Test this here: click this link and the script will try to work out what button you used.

Note that browsers may prevent you from using JavaScript dialogs (like alert) in right click or middle click event handlers, to prevent you from pointlessly breaking the browser's UI interaction.

There is also an oncontextmenu event supported by most current browsers (not available in all browsers; most obviously not supported in Opera 9-, but supported since Opera 10), that fires instead of the onclick event when the user right clicks, or when they press the context menu key, or otherwise trigger the context menu. This can be cancelled in order to replace the context menu with a custom context menu. Note, however, that the user may be able to prevent it being detected, and disabling it can make your page inaccessible for users who use the context menu for navigation. It is also entirely possible that a browser may not support it (since it is not part of existing standards), and browsers may not always trigger it for all ways of activating the context menu. Use it sparingly, and only for places where you want to provide a custom context menu that provides additional functionality relating to the item that was clicked. It should never be relied on as the only way to provide functionality.

<script type="text/javascript">

function showContextMenu( e ) {
  if( !e ) {
    //Internet Explorer
    e = window.event;
    //no need to check for its existence;
    //no browser that supports oncontextmenu will fail to provide event information
  }
  //... put code to get the mouse coordinates here (shown above),
  //and show the DHTML layer that will be used as the menu (shown in the DHTML chapter) ...
}

</script>

<a oncontextmenu="showContextMenu(arguments[0]);return false;" href="whatever">

Test this here: right click this link and the script will try to replace the context menu with an alert. (Note that since this attribute is not valid HTML 4, it is set using an event handler property in this test.)

Creating objects

Any function in JavaScript can be used to create custom object classes, simply by calling it using the keyword new. When called in this way, the special variable this inside the function references the new object that is being constructed (it normally refers to the 'current' object, which is usually window, except inside methods). The function should not return a value. The following function can be used as demonstrated to create an object of class myobject:

function myobject() {
  this.containedValue = 0;
  this.othercontainedValue = 0;
  this.anothercontainedValue = 0;
}

var mything = new myobject();

And there you go, mything is now an instance of class myobject. It will have the following properties, all of which will be 0:

You could also now write this:

myobject.prototype.newContainedValue = someValue;

This will cause all instances of class myobject will have the property newContainedValue with value someValue.

Use the instanceof operator to find out if your object is an instance of a given object class:

if( myvar instanceof Array ) { ... }
if( myvar instanceof myobject ) { ... }

Creating an object with methods

Now I will give you an example showing you how to create methods for your objects. As an example, I will create a circle as my object. The methods will be

nameOfCircle.retArea()
Returns the area of the circle (pi r2)
nameOfCircle.retCirc()
Returns the circumference of the circle (2 pi r)
nameOfCircle.mvBy(xDis,yDis)
Moves the circle by xDis in the x direction and yDis in the y direction

The following lines point the methods to functions that the object will use as the methods:

The third of these defines the method using an anonymous function in one line, and does not work in some early Netscape 4 releases. Note, it has a semicolon after the '}', and is one of the few places where this is correct practice.

function mycircle(x,y,r) {
  this.xcoord = x;
  this.ycoord = y;
  this.radius = r;
  this.retArea = getTheArea;
  //This next line uses an alternative syntax
  this.retCirc = function () { return ( Math.PI * this.radius * 2 ); };
  this.mvBy = mvCclBy;
}
function getTheArea() {
  return ( Math.PI * this.radius * this.radius );
}
function mvCclBy(xDis,yDis) {
  this.xcoord += xDis;
  this.ycoord += yDis;
}

/*
create a mycircle called testcircle where testcircle.xcoord is 3
and testcircle.ycoord is 4 and testcircle.radius is 5
*/
var testcircle = new mycircle(3,4,5);
/*
use the mvBy method to displace the centre of testcircle.
move it by 2 in the x direction and 3 in the y direction
*/
testcircle.mvBy(2,3);
//testcircle.xcoord is now 5 and testcircle.ycoord is now 7

window.alert( 'The area of the circle is ' + testcircle.retArea() );
window.alert( 'The circumference is ' + testcircle.retCirc() );

The special 'toString' method

All objects have the 'toString' method, even if you do not define it yourself. The method returns a string representation of the object, and is automatically called whenever a string representation of an object is required, such as when you use alert(myObject);

In most browsers, this returns '[object Object]', although some more useful browsers return a string like this:

'{property1:value1,property2:value2,method1:function () { ... },etc.}'

However, for your own objects, you may wish to provide a special string that may provide more useful information. To do this, simply define the 'toString' method:

this.toString = function () {
  return 'Circle object; xcoord: ' + this.xcoord + ', ycoord: ' +
    this.ycoord + ', radius: ' + this.radius;
};

Advanced object techniques

These deal with concepts that are rarely used in JavaScripting. JavaScript is a more powerful programming language than most people make use of, but that is because in normal scripting, these powerful features are not really necessary. If you are just learning, or if you are interested only in day-to-day scripting, I suggest you move on to the next chapter.

Some of these techniques may not work in older browsers like early Netscape 4 releases.

Adding extra properties/methods using prototype

Take the mycircle example from the top of this section (creating new objects). We have already created an instance of the mycircle class called 'testcircle'. And we can also assume that we have created a few other mycircles. Now, let us assume that we want to add another property to each circle. For example, we want each circle to have a 'texture' property. We could use this:

testcircle.texture = 'smooth';

And we could do this for each individual mycircle. But since we want to do this for all of them, it would be much more easy to give all instances the property at the same time. So we can use the 'prototype' property of the class constructor:

mycircle.prototype.texture = 'smooth';

Immediately, all of the mycircles that we have created will now inherit the new property:

alert(testcircle.texture);
//alerts 'smooth'

We can add new methods the same way:

mycircle.prototype.setArea = function (oArea) {
  this.radius = Math.sqrt( oArea / Math.PI );
};
mycircle.setArea(5);

This is most useful with instrinsic (fundamental inbuilt) objects. For example, the regular expression construct /a[0-9]b/g is shorthand for new RegExp('a[0-9]b','g'); and in fact the same is true for all intrinsic object classes, such as String, Number and Boolean. So if, for example, we wanted to create a new method on all strings called 'reverse' that returned their contents in reverse order, we could do this:

String.prototype.reverse = function() {
  for( var oStr = '', x = this.length - 1, oTmp; oTmp = this.charAt(x); x-- ) {
    oStr += oTmp;
  }
  return oStr;
};

The use of prototype could have been applied to create all of the methods of the mycircle object, not just new ones. This gives a mixed response to performance. It will not have to store individual copies of the methods for each instance of the object, so it may require less memory, but it will require the browser to search the current and parent scopes to find the methods. This may cause a marginal delay. Generally, you should use what is appropriate for your code, and not base this decision on performance (unless you are dealing with a very specific controlled environment):

function mycircle(x,y,r) {
  this.xcoord = x;
  this.ycoord = y;
  this.radius = r;
}
mycircle.prototype.retArea = function () {
  return ( Math.PI * this.radius * this.radius );
};
mycircle.prototype.retCirc = function () {
  return ( Math.PI * this.radius * 2 );
};
mycircle.prototype.mvBy = function (xDis,yDis) {
  this.xcoord += xDis;
  this.ycoord += yDis;
};

In some cases, it may be desirable to work out if a property on an object is attached to the object instance itself, or somewhere in its prototype chain. In JavaScript, all objects have a method called hasOwnProperty that returns true if the given property is attached to the individual object instance. Of course, it is also possible to check if the object's constructor also has the same property with the same value as the object instance itself, but that can give the wrong result if separate properties exists with the same value on both the object instance and the prototype chain. The hasOwnProperty method accepts a single parameter; the name of the property as a string.

function myclass() {
  this.propertytwo = 2;
  this.propertythree = 3;
}
myclass.prototype.propertyone = 1;
myclass.prototype.propertytwo = 2;

var myinstance = new myclass();
myinstance.propertyfour = 4;

alert(myinstance.hasOwnProperty('propertyone'));
//alerts false

alert(myinstance.hasOwnProperty('propertytwo'));
//alerts true

alert(myinstance.hasOwnProperty('propertythree'));
//alerts true

alert(myinstance.hasOwnProperty('propertyfour'));
//alerts true

alert(myinstance.hasOwnProperty('propertyfive'));
//alerts false

Public and private properties

This is a concept that is almost never used in JavaScript, and there is a good reason. It simply is not necessary. Even complicated scripts are almost never complex enough to require this level of control. However, many programmers familiar with other programming languages (such as Java or C++) often desire this behavior in JavaScript simply because it is a concept they are familiar with. This sort of concept is only really useful when putting together large projects from multiple pieces of code.

Say for example that I am producing a public functions library. A set of constructors and methods that people can include in their own projects (something that Java programmers use all of the time). Say for example that the mycircle constructor is included in it, so that you can create your own mycircles. Now say for example you try this:

var aCircle = new mycircle(5,6,'about 3mm');

That will work now (it's not a valid measurement, but my constructor will not argue), but later on when you try to use the methods that my class provides, it will fail. So I can check what you have provided, and make sure it is a valid number, and use a default value if it is not. But then you can say something like this:

aCircle.radius = 'some text';

Again, it would break (of course, it is your own fault, but in more complicated applications, it would be possible to make a mistake where this causes a real problem). So what I want to do is to not allow you to modify that property directly, and only allow you to change it using a method that I control:

this.setRadius = function (oRad) {
  if( typeof(oRad) == 'number' && oRad >= 0 ) {
    this.radius = oRad;
  } else {
    this.radius = 0;
  }
};

An alternative situation where this can be important is if I am storing the information in one set of properties. I then upgrade my libraries, maybe adding some extra functionality. But in order to do so, I have to change the properties that I am using. If your script was relying on them existing in a specific format, and I have now changed that format, your script would fail. If I could protect those properties, and force you to use only methods, then I could do whatever I needed with the properties, and as long as I then change the methods to use the new properties, your script would keep working, and you would not even need to know what I had changed or why. That way, we could each manage our own project, and not waste each other's time.

It would also help avoid possible conflicts between undisclosed properties, and your own scripting. For example, if you decide to temporarily assign a property to an object, but you were unaware that internally, my object constructor already used that property name, you would overwrite my object's property, and cause problems with the object.

This is what private properties are for. They do not allow scripts that use the contructor to use or modify the properties directly. They only allow the methods of the object itself to use and modify them. Unlike many other languages, JavaScript does not declare each variable type as 'public' or 'private'. It all depends on how you create them.

Saying 'this.propertyname' as I did above will create a public property. Any script can create an object then use and modify its properties directly. Using 'var' to define a variable in the constructor will create a private property. Note that unlike public properties, the private properties are then accessed, including from within methods, without the 'this.' prefix - just like a normal variable. This makes heavy use of JavaScript's scope functionality. Private variables can only be accessed from methods that are declared inline, and not externally referenced or created using the prototype construct. Methods of this kind are also known as privileged methods. An example of its use might be: this.mymethod = function () { alert( propertyname ); };

function myob() {
  this.property1 = 'value1'; //this creates a public property
  var property2 = 'value2';  //this creates a private property
  this.method1 = function () { alert( property2 ); };
}
var oneOb = new myob();
alert(oneOb.property1); //alerts 'value1'
alert(oneOb.property2); //alerts undefined (private property)
oneOb.method1();        //alerts 'value2'

Similarly, you can also create private methods. These are simply a function that you create inside the constructor function. This may look confusing, but it works. The private function can only be called by the constructor itself, or by methods that are defined inline. Private methods can be used as public methods, if they are assigned to a public method constructor, and accessed using the public method constructor (as with 'method2' below).

function myob() {
  function cantBeSeen() {
    alert(secretValue);
  }
  var secretValue = '';
  this.method1 = function () {
    secretValue = 'no surprises';
    cantBeSeen();
  };
  this.method2 = cantBeSeen;
}
var oneOb = new myob();
oneOb.method1(); //alerts 'no surprises'
oneOb.method2(); //alerts 'no surprises'

For more information on private, public and privileged properties and methods in JavaScript, see Douglas Crockford's 'Private Members in JavaScript' page.

Sub-classes and class inheritance

This is also hardly ever used in JavaScript, despite its popularity in other languages. Take this as an example. I want to make a new type of object, that will store the data to represent a sphere (a ball - in case you don't know). Since a sphere is just a three dimensional circle, I would probably want to include all the methods I created earlier for the circle object; retArea to get the cross-section area, retCirc to get the circumference, mvBy to move it by a certain amount. Aditionally, I would probably want retVol to get the volume, and retSurf to get the surface area. I would also need to provide a z coordinate when creating the object, and again when calling the mvBy property.

So, I want to create a new type of object, based on the mycircle, but with a few aditions and modifications. I will call this new type of object a mysphere. Now, I could just rewrite all the code I already wrote for the mycircle and change the bits I need. But in a real-world application, this might be a lot of wasteful duplication, for only one or two modifications. So what I want to do is make the mysphere inherit all the properties and methods of the mycircle, then overwrite only the ones I want.

This effectively makes mysphere a sub-class of mycircle, and making the mysphere class constructor inherit from the mycircle class constructor is as simple as this:

function mysphere(x,y,z,r) { ... constructor code ... }
mysphere.prototype = new mycircle();

In case you are wondering, the way it works is to actually create a mycircle, then assign that to the mysphere constructor prototype. As a result, the mysphere constructor has the mycircle object added to its prototype chain. What this means is that each time a mysphere is created, it will inherit the properties and methods of the mycircle object. It can then override any of these properties using its own prototype, or with properties and methods created within its own constructor. If any of these properties are subsequently removed (using the 'delete' keyword), the inherited ones will become available again. That also means that if the mycircle prototype is changed (properties are added or deleted, etc.), these changes are replicated down the chain, so the mysphere also inherits these changes.

Note that the prototype chain is established for each object as the object is created. Once an object has been created, its prototype chain cannot be changed (though new properties and methods can still be added to its parent classes, and those changes will be reflected in the object). Changes to its prototype chain will have no effect; it will still see the old prototype chain. The new chain will only be used for new instances of that class. This is also the case for the assignment of a new mycircle to the prototype of mysphere (which, as stated, creates a new instance of the mycircle class); any subsequent changes to the prototype chain of the mycircle class will not be reflected in the mysphere class. For this reason, it is very important to ensure that the prototype chain is built in the correct order, with class constructors and their prototypes set up before any child class is made to inherit from them. Properties and methods can still be added to any class or parent class at any time after their own prototype chain has been set up.

The line that makes mysphere inherit from mycircle sounds simple enough. And at least, it would be, but you will remember that mycircle expected me to pass it some parameters that it would then use. Then I call my new constructor, and these parameters will not automatically be passed to the mycircle constructor function. I need to do this myself for each mysphere that I want to create. But since I am not passing these parameters when assigning the mycircle object to the mysphere prototype, I also need to ensure that the mycircle contructor stops before attempting to create its own properties. Instead of defining the public properties immediately, I will define them using a method (that I have decided to call getready), and if the required parameters have been passed (arguments.length), I will immediately call the method.

If the parameters are present, the mycircle constructor keeps working, and no-one needs to know that anything was changed at all. To make things easier later on, I will take all of the methods out of the constructor, and add them later using the prototype. That way, the mycircle prototype methods will always be available without me needing to create a new mycircle. Unfortunately, this also means that public and private properties are very hard (or impossible) to use. It's a trade off - one functionality for another.

function mycircle(x,y,r) {
  if( arguments.length ) { this.getready(x,y,r); }
}
mycircle.prototype.getready = function (a,b,c) {
  this.xcoord = a;
  this.ycoord = b;
  this.radius = c;
};
mycircle.prototype.retArea = function () {
  return ( Math.PI * this.radius * this.radius );
};
mycircle.prototype.retCirc = function () {
  return ( Math.PI * this.radius * 2 );
};
mycircle.prototype.mvBy = function (xDis,yDis) {
  this.xcoord += xDis; this.ycoord += yDis;
};

Now, back to the mysphere constructor. I have already said how to make it inherit, but it still needs to run the mycircle.getready method to initialise the properties. To do this, we will need to reference the parent class prototype, and run the method ourselves. This also ensures that even if each parent class uses the same method name (getready), the correct one is always referenced. Since we can almost never be sure how many sub-classes we will need, it is useful to do it this way, and simply use the same method name for each, knowing that we will not have problems with name conflicts.

When running the method, we need to tell JavaScript that even though we are referencing a method for a different prototype, we want to run it as if it were a method of the object we are creating (to make sure that any properties it creates are added to the object we are creating). This could be done using the 'call' method or 'apply' method, but unfortunately, Internet Explorer 5 does not understand these, so I will assign it to a temporary property of the new object, and run it from there.

To reference the method from the parent class prototype, it would be possible to use the constructor's prototype chain to locate it (the property that references the constructor will, at this point, refer to the parent class - more on that later):

this.constructor.getready

However, that will only work for one level of inheritance, due to the way it is run as a method of the current object for all child classes. As a result, it is best to reference it by name, which will work no matter how many levels of class inheritance are used:

mycircle.prototype.getready

Note that when assigning the mycircle object to the mysphere prototype, it also overwrites the mysphere prototype constructor property. This is not a major problem, but some scripts use it, so we will put the reference back where we want it:

function mysphere(x,y,z,r) {
  if( arguments.length ) { this.getready(x,y,z,r); }
}
//inherit from the mycircle prototype
mysphere.prototype = new mycircle();
//put the correct constructor reference back (not essential)
mysphere.prototype.constructor = mysphere;

mysphere.prototype.getready = function (a,b,c,d) {
  //reference the getready method from the parent class
  this.tempReady = mycircle.prototype.getready;
  //and run it as if it were part of this object
  this.tempReady(a,b,d);
  //now that all required properties have been inherited
  //from the parent class, define extra ones from this class
  this.zcoord = c;
}
mysphere.prototype.mvBy = function (xDis,yDis,zDis) {
  //override the existing method
  this.xcoord += xDis;
  this.ycoord += yDis;
  this.zcoord += zDis;
};
mysphere.prototype.retVol = function () {
  return ( 4 / 3 ) * Math.PI * Math.pow( this.radius, 3 );
};
mysphere.prototype.retSurf = function () {
  return 4 * Math.PI * this.radius * this.radius;
};

And finally, to use it:

var testsphere = new mysphere(3,4,5,6);

alert( 'The cross-section area is ' + testsphere.retArea() );
alert( 'The circumference is ' + testsphere.retCirc() );
alert( 'The volume is ' + testsphere.retVol() );
alert( 'The surface area is ' + testsphere.retSurf() );

Test this class inheritance script.

There is no limit to how many sub-classes can inherit from a class, and there is also no limit to how many levels of sub-classes can be created. As an example, I will create a ball class that will inherit from the mysphere class:

function ball(x,y,z,r,m) {
  if( arguments.length ) { this.getready(x,y,z,r,m); }
}
ball.prototype = new mysphere();
ball.prototype.constructor = ball;

ball.prototype.getready = function (a,b,c,d,e) {
  this.tempReady = mysphere.prototype.getready;
  this.tempReady(a,b,c,d);
  this.mass = e;
}
ball.prototype.retDensity = function () {
  return this.mass / this.retVol();
};

Test this class inheritance script.

The instanceof operator returns true when testing an object against any class in its prototype chain, so it would say that an instance of the ball class was also an instance of the mysphere class, and also the mycircle class.

For more information on sub-classes and class inheritance in JavaScript, see Kevin Lindsey's class inheritance page.

Static methods and properties

A static property (or method) is a property of the class constructor, not an individual instance of that class. For example, mycircle.myproperty instead of (new mycircle()).myproperty. While not often used in JavaScript, they are often encountered in other programming languages, and are covered here for those who are familiar with them in other languages. As with most objects in JavaScript, the functions used as class constructors can have properties added to them, and these will be public static properties:

function mycircle() { ... }
mycircle.aStaticProperty = true;
mycircle.aStaticMethod = function () { ... };

Making a private static property is significantly harder, as it is a concept that JavaScript (at least in widely supported implementations) simply does not recognise. However, it is possible to provide an approximate equivalent to this functionality by using an anonymous wrapper function to create a scope where the private "properties" are created as normal variables, then having that return another function that will be used as the constructor. As long as the constructor function is declared inside the anonymous function, it will have access to the private variables from the anonymous function's local scope when it is executed. This structure is described in the section on writing functions.

var mycircle = (function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  function publicConstructor() {
    this.normalInstanceProperty = 1;
    ...
    alert(privateStaticVariable);
  };
  publicConstructor.prototype.normalProperty = 2;
  return publicConstructor;
})();
mycircle.publicStaticProperty = true;
mycircle.publicStaticMethod = function () { ... };

Singletons

The singleton is one of the design patterns that is commonly seen in other languages, and in a simplified format within JavaScript. The idea of a singleton is to have a class that can only have one object created from it. Every time a script tries to create another instance of that class, it returns the same copy as before, so if the properties are modified on one of them, then the other will also see the modifications. The use of singletons can be controversial, but I will not cover that here. This tutorial will only show you how to produce them.

In JavaScript, there is the ability to create a generic anonymous object to serve this purpose. It can either be created using new Object() or the shorthand { ... } syntax, and can then be given any properties or methods that are needed. However, this does have limitations; using a generic object means that it is not possible to use private properties. In most cases, this is functionality that is not needed, and the majority of scripts simply use a generic object. Instead of trying to constuct new instances of the object, scripts will just create another variable that references the object:

var mySingleton = { foo: 'bar', baz: 'qux', aMethod: function () { ... } };
...
var anotherReference = mySingleton;

If you feel the need for private properties, read on. With other languages, the expectation is to create a class constructor that refuses to be instantiated by other scripts, and provides a public static method that constructs the object the first time it is called, and returns the same object for each subsequent call. JavaScript, however, does not provide a way to prevent a class from being instantiated (at least in widely supported JavaScript). If it exists as a function, it can be called as a constructor.

It is tempting (and sometimes used) to simply use an anonymous function as the class constructor, construct it once, and use its scope to keep variables private. This is limited to normal private properties, and cannot be used for private static properties. The theory is that this enforces the singleton principle by preventing another attempt to reference and use the class constructor. This pattern fails to produce a singleton, however, as other code can simply use the singleton's constructor property to reference it - although you could override the singleton's constructor property, this can cause other problems later on if code needs to be able to use that to add properties to the constructor.prototype.

var mySingleton = new function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  ...
};
var newInstance = new mySingleton.constructor();

It is, however, possible to adapt the pattern used for private static properties, to create private properties for the singleton, while at the same time creating a proper singleton. Once again, an anonymous function is used to contain the private variables and functions. However, instead of returning a class contructor function, it just returns the single object:

var mySingleton = (function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  return { ... };
})();

That is, of course, a very simple pattern, but it shows something important; by returning a generic object, it is not possible to use new mySingleton.constructor() to create a separate instance, as that would just produce a generic object. This particular format is quite awkward, however, as the generic object prevents the prototype from being used to cleanly add more properties and methods. It is very simple to extend it to make that possible, however, just by creating the class contructor function inside the anonymous function:

var mySingleton = (function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  function PrivateConstructor() {}
  PrivateConstructor.prototype.publicProperty = true;
  ...
  return new PrivateConstructor();
})();

This now means that new mySingleton.constructor() will create a new instance of a PrivateConstructor object. However, the constructor function, when called in this way, is pulled out of the inner scope during that call, and as a result, it ends up with a relatively useless object that has none of the prototype's properties. Once again, the singleton principle is enforced. New properties can be created by modifying the mySingleton.constructor.prototype. The singleton can itself be used as the prototype for another constructor, to allow sub-classes to be created.

There are some reasons why this particular approach may not be favoured; specifically that it always creates the singleton even if it is never used. Some may also prefer to use a function call instead of having a global variable always created (though a function is still a global variable), as this may make it possible to change the implementation later to use a different design pattern instead of a singleton. It is possible to take care of both of these concerns, by returning a function instead of an object. That function can check if the singleton has been created yet, store it in a private variable, and return the instance. This has the small limitation that an instance of the object will need to be created by an external script in order to add properties or methods to the prototype, but this is a limitation that is unlikely to be a problem:

var getSingleton = (function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  function PrivateConstructor() {}
  PrivateConstructor.prototype.publicProperty = true;
  ...
  var onlyInstance;
  return function () {
    if( !onlyInstance ) {
      onlyInstance = new PrivateConstructor();
    }
    return onlyInstance;
  };
})();
...
var mySingleton = getSingleton();

Or an equivalent approach that assigns the function to a global variable instead of returning it (this may be preferred for clarity, or to allow you to put the class instantiation methods at the top of the anonymous function instead of the bottom of it):

var getSingleton;
(function () {
  var onlyInstance;
  getSingleton = function () {
    if( !onlyInstance ) {
      onlyInstance = new PrivateConstructor();
    }
    return onlyInstance;
  };
  function PrivateConstructor() {}
  PrivateConstructor.prototype.publicProperty = true;
  ...
})();

It is, however, possible to make the anonymous function return an object which can be used for prototyping directly, but this becomes quite messy. The PrivateConstructor can be made to inherit its prototype from the object that will be returned by the anonymous function. The singleton (when it is created) will then inherit any changes made to that object (in addition, it will inherit the getInstance method from the returned object, but this is harmless). I recommend that you avoid getting into a situation where you would need this functionality in a script.

var singletonMaker = (function () {
  var privateStaticVariable = true;
  function privateStaticMethod() { ... }
  var onlyInstance;
  var returnedObject = {
    getInstance: function () {
      if( !onlyInstance ) {
        onlyInstance = new PrivateConstructor();
      }
      return onlyInstance;
    }
  };
  function PrivateConstructor() {}
  PrivateConstructor.prototype = returnedObject;
  PrivateConstructor.prototype.publicProperty = true;
  ...
  return returnedObject;
})();
...
singletonMaker.foo = true;
var mySingleton = singletonMaker.getInstance();
alert(mySingleton.foo); //true

For more information on this topic, see Kai Jäger's article about singletons in JavaScript.

ECMAScript 5

ECMAScript 5 adds several additional methods for custom objects, allowing you to control whether properties can be added, or obtain information about the object's prototype. ECMAScript 5 is not currently supported by enough browsers for it to be used in most practical applications. Even the parts that are supported by Internet Explorer 9+ are not available in quirks mode - the browser intentionally regresses to IE 6's scripting support. This means that generic scripts that are designed to work anywhere, will not be able to make use of the new functionality even once it becomes widely supported.

Creating time delays

There are two ways of creating time delays with JavaScript. The first is more simple and will simply wait for a specified amount of time before executing a function. The second does the same but will repeatedly execute the function.

Note, many browsers have a minimum delay length of between 25 and 75 ms, with some of the fastest browsers having a minimum delay of about 3 ms. If a shorter delay is specified, the actual delay will be the minimum delay length. Even with higher numbers, the delay is never perfect. Most browsers will take slightly longer than the time you ask for, typically just a few miliseconds error. Some may correct their errors over time with interval timers. Also note, setting many timers with short delays on one page will cause the browser to become slow and somewhat unresponsive. Three or four timers is usually the reliable limit.

setTimeout

The first method uses a window method called setTimeout(). The method waits for a specified number of milliseconds then executes the specified code. The code can either be a direct reference to a function, or it can be a string that will be evaluated as if it contained source code.

window.setTimeout(referenceToFunction,timeInMilliseconds);
window.setTimeout('runMoreCode()',timeInMilliseconds);

Wherever possible, you should use a direct function reference as it is much more efficient. Using a string requires the browser to create a new script environment so it can process the script.

If you create a timeout, the code after the call to setTimeout will continue to run as normal. After the specified delay (and once the old thread has finished), the timeout will start a new thread, and the code specified in the call to setTimeout will be run in the new thread, after any code that was still running in the initial thread. Unlike with many more complex languages, JavaScript does not offer any way to control when those threads sleep, wake, or yield. The JavaScript engine handles all of that, and you must accept that your new thread should execute after the end of the current thread. Although in theory, a multi-threaded script engine could execute the new thread at the same time as the old thread, JavaScript engines operate as a single-threaded application, and will simply allow one thread to complete before allowing the other thread to start. The same applies to events, which run in their own threads, and can be triggered at any time - they will wait until existing threads have finished before being started.

To pass variables to a timeout, you can use either format. To use a string, it is necessary to make sure the variables you want to pass can be represented in one of the primitive data types, and do not contain any characters that will break the string format (such as quotes). If possible, you should avoid this format, but this is an example, just in case you need to:

window.setTimeout('runMoreCode(\''+someString+'\','+someNumber+')',10);

The direct function reference is much more easy, as you can pass variables as extra parameters to the setTimeout method. In addition to being more easy, it is also able to accept any type of variable. Note, however, that this does not work in Internet Explorer. Be careful with browsers that use the Mozilla Gecko engine (such as Firefox and Netscape 6+) as they will always pass an extra parameter to the function - the number of miliseconds error. This example uses an inline anonymous function, and is equivalent to using a direct function reference:

window.setTimeout(function (a,b) {
  //do something with a and b
},10,someString,someObject);

The variables that are passed to the function will be the values held by the variables at the time that the method was called. For example, assume you had a variable called myVar and it contained the number 5. You then called a timeout, passing it the variable myVar, and set it for a delay of 1 second. Immediately you change the value of myVar to 7. When the timeout fires, it will run the function, and pass it the number 5, since that was the value myVar contained when you called the function.

Unfortunately, since Internet Explorer does not support the functionality to pass additional parameters to a directly referenced timeout function, the most compatible approach is simply to make sure that the variables are kept in the current scope, so that the timeout function can reference them directly.

setInterval

The setInterval method is identical in syntax to setTimeout(). The difference is that as well as firing after the specified delay, it will fire again after the same delay, and will continue to fire at the specified interval until it is cancelled.

window.setInterval(function (a,b) {
  //do something with a and b
},10,someString,someObject);

Clearing timeouts and intervals

You can cancel a timeout or interval by calling the relevant clearTimeout or clearInterval method, passing it a reference to the interval object. The code specified in the timer will not be run. In this example, an interval timer will be set to fire once every second (it will perform a useless task of incrementing a number, just as an example), and a timeout will be set to cancel the interval after 3.5 seconds:

var myInterval = window.setInterval(function (a,b) {
  myNumber++;
},1000);
window.setTimeout(function (a,b) {
  clearInterval(myInterval);
},3500);

You can obtain the timeout object in the same way as shown here for the interval, and you can cancel it in the same way, using clearTimeout.

Using cookies

Cookies are variables that can be stored on a user's computer and be picked up by any other web pages in the correct domain and path. Cookies are set to expire after a certain length of time. They are limited to storing string values only.

Be warned that many users (including me) will not permit cookies on their computers. Do not make your web sites rely on them. The reason for this is that many web sites only use cookies as part of advert tracing systems, which they use to track your movement around the Internet. I would not want anyone to follow me around a city while I was shopping, taking notes of every shop I visit and whether I look in the lingerie section, as that would be an invasion of my privacy. Many people feel the same applies to the Internet. You may find it helps to firstly display a message saying what you are going to use the cookie for, for example to store a username and password for the next time they visit the site.

Note also that European law requires sites to gain explicit permission before using cookies, unless those cookies are essential to the operation of the site (such as a shopping basket).

Some browsers will have a limit to how many cookies they can store, usually 300 cookies or more, of which there may be 20 cookies per domain name. A total of 4 KB (after encoding) of cookies can be stored for any domain or path.

The document.cookie object is a string representation of all cookies available to the current web page. The document.cookie object is somewhat unusual in that when you assign string values to it, it does not become the value that you assign to it. Instead, it takes a part of that value and appends its current value to that, so making a string containing several pairs of variableName=variableValue.

Creating / modifying and deleting cookies

I have also written a cookie script to handle all of this for you.

Cookies are created or modified by assigning a name=value pair to the document.cookie object:

document.cookie = cookieString;

The cookieString is more than just a single value. As well as a value, it can contain other information, such as when you want the cookie to expire, and what file paths it should apply to. Any of these values that you want to specify should be put together in a string as keyword=value;keyword=value;etc. and assigned as a single string to document.cookie. Only the cookie name and value are required, all other values are optional. The format of a complete cookie string is:

cookieName=cookieValue[;expires=dataAsString[;path=pathAsString[;domain=domainAsString[;secure]]]]

This is an example of how to set a cookie called mycookie, and assigning it a value of hello, with an expiry date of 17 December 2010, at 10:00 in the morning:

document.cookie = 'mycookie=hello;expires=Fri, 17 Dec 2010 10:00:00 GMT';
cookieName and cookieValue
These are strings, and must be URL encoded. They and can contain any characters. To URL encode a cookie name or value, you can use the escape method. Unfortunately, this cannot cope with unicode characters. Newer browsers will also provide the encodeURIComponent method that is able to cope with unicode, but if you use this, you will lose support for older browsers.
expires
This must be written in full. The Date.toGMTString() method can return dates in the correct format for you. For example:
var theDate = new Date();
var oneYearLater = new Date( theDate.getTime() + 31536000000 );
var expiryDate = oneYearLater.toGMTString();
Once the specified date is reached, the cookie expires and is deleted. If expires is not specified, the cookie will be deleted when the browser is closed. If expires is set to a date in the past, the cookie is deleted immediately. This is how to delete a cookie (some browsers may take a few seconds to actually delete the cookie). In theory, computers should be able to accept any future date but in reality, UNIX computers will not currently accept a date after 03:14 on 18 Jan 2038 and many Macintosh computers will not currently accept a date after 06:28 6 Feb 2040 or the same date as that for UNIX. These are the UNIX and Macintosh equivalent of the millennium bug.
path
This gives the path or directories that the cookie should be accessible from. The default is the current path. Alter this using '../' (up one directory), '/' starting at the base directory and 'subdirectoryName/' to start from the 'currentDirectory/subdirectoryName/'. NOTE, if two cookies are set with the same name but different paths, both cookies will be permitted. If the script is in a directory where both those paths are satisfied, both cookies are available, and may be returned in any order. There is no way to distinguish between them. This can make scripts difficult to write so be careful!
domain
gives the domain that the cookie is accessible from. The default is the current domain. The rules reguarding what may be put here were originally written very strictly. Domain names ending in com, edu, net, org, gov, mil, and int must contain at least 2 '.' characters (eg. www.google.com). Any other domain names must contain at least 3 '.' characters (eg. www.howtocreate.co.uk). The domain should only be the current domain or a subdomain of it. Many browsers will now accept any valid domain name.
secure
Having this written means the cookie is only accessible on sites with a secure (https) connection.

Reading cookies

When document.cookie is used to retrieve cookies, they are returned in the following format:

cookieName4=value; cookieName3=value; cookieName2=value; cookieName1=value

Note that the final variable value does not end with a semicolon. If there is no value for a variable, some browsers will give 'cookieName=' but some will only give 'cookieName'. Cookies are only available through the document.cookie object and cannot be accessed by simply typing their name (unless you retrieve the cookie and define that as a variable yourself).

When trying to retrieve values from this string, it is important to ensure that you do not mistake cookies for each other. For example, when searching for 'myname' it is important that you do not find a cookie called 'notmyname' or 'mynamehere'. There are many scripts that get this wrong, but the algorithm I will show you below does not make these mistakes.

The simple way to do this is to use the split method, to split on the cookie separation value '; ' (including the space). This will return an array of strings, each of which represents a name=value pair. You can then use a 'for' loop to cycle through all the array cells, splitting each of them on the '=' string. This returns an array containing one or two cells. The first will be the cookie name, and the second (if it exists) will be the cookie value.

Use either the unescape or decodeURIComponent methods to decode the contents of the first cell, containing the cookie name. If that matches the desired name, then you have found the correct cookie. Use if(!arrayname[1]) to check if the second cell has a value. If not, the cookie's value is an empty string. If it does have a value, then you can decode it to work out what it is. You can then break out of the 'for' loop.

If you reach the end of the first array without discovering the required cookie, then the cookie does not exist.

Security

JavaScript is designed as an open scripting language. It is not intended to replace proper security measures, and should never be used in place of proper encryption. See also my article about cross site scripting.

JavaScript has its own security model, but this is not designed to protect the Web site owner or the data passed between the browser and the server. The security model is designed to protect the user from malicious Web sites, and as a result, it enforces strict limits on what the page author is allowed to do. They may have control over their own page inside the browser, but that is where their abilities end.

Most people who want to know about security with JavaScript are interested in producing password protected pages or sending encrypted data to or from the user's computer. For true security, use SSL/TLS (HTTPS) and put all of your checks on the server. You could also use a security lockout if too many false attempts are made, preventing brute force cracks. JavaScript cannot replace this functionality. The problem lies in the fact that if a person can read what you are sending over the internet, they can also rewrite it. So when you think you are filling in a password to access a protected page, they have changed it so that you are actually filling in a password that will be sent to them. This requires SSL to be sure that you are protected. Still, this tutorial is about JavaScript, so I will now show you what can and cannot be done with JavaScript.

Protecting the source of your scripts

Oh dear. This is just not possible. Many people make futile attempts to do so, but to be honest, there is no point in trying. In fact, in many developers' opinions, there is no such thing as copyright with JavaScript, although it is theoretically possible. The point with copyright and patents is that you can only copyright or patent something completely new, a new innovation, something that has not been done or written before. You can almost guarantee that nothing you do with JavaScript will be a new innovation or even newly written. Someone will have done it before, almost certainly using the exact same algorithm with just a few variable names changed. JavaScript is just not designed for innovative programming since it just uses APIs designed by someone else to do what you are doing, and they already came up with it before you in order to invent the API. Even if you write something in a "new" way, it will still be doing something that has already been done, and if you did attempt to take things too far and take the matter to court, you would just be laughed back out of it again.

As for protecting what you send, JavaScript is passed in text, not compiled to a binary first, so the code is always visible. How can you stop people seeing the source when you are sending the source to each viewer? Let me walk through the problem.

If the source of the JavaScript is held in the page you are viewing, a simple "view source" will show you the script. Looking in the browser's cache will show the scripts that are in header files. Of course you need to check the source first to find the name of the header file.

Many developers have spotted the fact that both of these methods require the "view source" to be available, so they prevent the viewer from viewing the source. They do this by preventing the context menu from appearing when the user right clicks and by removing menus by using window.open etc. Believe me, both of these are useless. You cannot stop right clicks in all browsers (even in some where you can, the user can prevent scripts from blocking it). So some people try to prevent these browsers from viewing the page by using browser sniffing. This is equally uneffective. All the viewer has to do is swich off script when they get to the page, or view the source of previous pages to find the location of the protected page. In adition, Opera, Mozilla/Firefox, Safari and Internet Explorer are all capable of running user scripts that allow the user to override restrictions made by the page.

Some people even try to make sure that the page is only delivered if a referrer header is sent to make sure that the user came from the right page, and is not attempting to type in a location manually. So the user can use Curl, a program that allows them to request a page with referrer header, cookies, form fields etc., and save the download to a text file.

Some people try to encode the script using charCodeAt or escape, but as the decoding technique is provided in the page, only simple modifications are required to make the script appear in text, not as embedded script. I have seen one set of scripts that have been "protected" by changing their variable names to completely incomprehensible names, and adding several redundant lines of incompressible code and removing all redundant spaces and linebreaks. It does not take too much work to turn this back into understandable code.

You may want to protect your code, but it simply is not possible. Someone who is determined will be able to find it out.

Password protecting a file

It is best to do this with a server side script, and an encrypted connection. But since this is JavaScript ...

Take the following for example. I want to only allow someone to access my page if they put in the correct password. I want to provide a box for them to write it, and then I want to test if it is correct. If it is, I let them view the page. The problem is that in the source of the page, I have to write the password in the script to test what they have written. For example:

if( document.forms[0].elements[0].value == 'mypassword' ) {
  location.href = 'protectedpage.html';
}

As described in the above section, you cannot protect the source of a page, especially from someone who is really determined. There is no point in trying. Once a user managed to see the source, they could see the password or the URL in plain text, or encoded, but again, that is easy to break.

For simple security, try this technique. Name the file to be protected whateverYourPasswordIs.html and make sure there is an index.html file in the same directory. Now use the following:

<form action="" onsubmit="location.href = this.elements[0].value + '.html'; return false;">
<input type="text">
<input type="submit" value="Submit">
</form>

The problem with this technique is that the page is still passed in plain text across the Internet, as is the name of the page that you send. If anyone is snooping at data packets on the Internet, they can retrieve the page's contents. In many places, packet snooping is illegal, but that does not mean that no-one does it.

This protection technique is known as security by obscurity, in other words, it is only secure because no-one knows it is there. If someone was determined, they would find it.

As a more complicated solution, try creating your own encryption technique that uses a password as an encryption key. Encrypt the contents of the file. As the page loads, use window.prompt to ask the user for a key. Try decrypting the page with their key, using document.write to write the page. If your technique is good enough, wrong passwords would only produce an incomprehensible output. With this technique, the password is never transmitted over the internet in plain text, and neither is the content. This technique could be cracked by brute force, trying every possible password until something works. Better passwords and encryption algorithms will help, but if someone was determined, they would break it. One of my readers has submitted a script to do this based on RC4 and Base64.

I have used both of these techniques, but never for anything really worth protecting. If you have something worth protecting, it is worth protecting properly; server-side, with an encrypted connection.

Encrypting data before it is sent to you

Normally, this cannot be done with JavaScript using the Internet alone, and should always be done properly; using a proper SSL connection.

You can, in theory, encrypt text at the user's end and unencrypt it at your end. The problem is that the user has to encrypt it with a password that you know so that you can unencrypt it. They would have to tell you by telephone or post. Alternatively, you could put the password in the source of the page and get the function to encrypt using that key. But this password would have to be sent over the internet in plain text. Even if you did encode it, it would not be too much work for a snooper to crack it. In fact, the encryption could even be broken with brute force techniques. So what do you do?

The best possible technique that could be done with JavaScript alone would be to create a symmetric encryption key using a twin public/private key pair as with techniques such as Diffie-Hellman or SSL, or use an asymetric public/private key pair and encryption technique as with PGP or RSA. The problem is that in order to prevent brute force cracking techniques, these require the browser to handle numbers as high as 2x10600 or higher. JavaScript is just not natively capable of working with numbers as high as this. As yet, I have found no solution to this, although on http://shop-js.sourceforge.net/ there is an algorithm for emulating large number handling, and an example of JavaScript powered RSA. The technique seems to work and takes only a few seconds to create keys, by using complex mathematics and algorithms (look at the source of crypto.js) to emulate large number handling.

Even so, if doing the equivalent of RSA (etc.), it is still not possible for the user to verify your identity as with SSL certificates, so it would be possible for a third party to inject their own code and have the information sent to them instead, without the user's knowledge. For the best security, stick to real SSL.

Protecting your email address

This is one of the very useful things that JavaScript can do. For those that don't understand the problem, I will summarise. Search engines "crawl" the Internet, following the links in pages and requesting other ones so that they can add the pages to their search databases. Using the same technology, spammers crawl the Internet looking for email addresses, whether in mailto: links or just written on the page. These email harvesters are one of the most annoying uses of otherwise useful technologies.

Simply writing your email address on any web page (through newsgroup postings etc) can leave you flooded with unsolicited emails. Many people fall into the trap of replying to these emails asking to be removed from the mailing list, and succeed only in confirming that their email address is valid. The problem is that you may actually want your email address on the page, or a link that automatically opens up a new email to you. There are a couple of steps you can take to prevent the problems with unsolicited emails:

Using JavaScript to write your email address

I have never heard of an email harvester that is clever enough to interpret JavaScript, though it is possible. All they usually do is read the text that makes up the page. So if you write your email address with JavaScript, they will not be able to read it. Remember that if you write the email address as a single word, even in the JavaScript, they may still interpret it as an email address, so it helps to break it up a little:

var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( 'My email address is ' + theName + '@' + theDomain );

This outputs: My email address is myEmailName@myEmailDomain.com

You can also use a mailto link:

var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<a href="mailto:' + theName + '@' + theDomain + '">Contact me<\/a>' );

This outputs: Contact me

You could even use a combination of both:

var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<a href="mailto:' + theName + '@' + theDomain + '">' + theName + '@' + theDomain + '<\/a>' );

This outputs: myEmailName@myEmailDomain.com

There is, however, a problem with this approach. It relies on your viewers having JavaScript enabled. Many of your more web-aware viewers will not. In my case, these are often likely to be people who I want to contact me. Fortunately, these viewers are the ones who are likely to understand what to change if you tell them to as I have showed above (in the bullet points). So, you can use a combination of both approaches:

<script type="text/javascript">
var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<p><a href="mailto:' + theName + '@' + theDomain + '">' + theName + '@' + theDomain + '<\/a><\/p>' );
</script>
<noscript>
  <p><a href="mailto:myEmailName(replace with @ symbol)myEmailDomain.com">
  myEmailName(replace with @ symbol)myEmailDomain.com</a></p>
</noscript>

In your browser, this outputs:

Semicolons

For use of the semicolon in the 'for' loop, see the section on control structures.

If you have managed to get through this whole thing and still not worked out where to put semicolons, here's how to work out where to put them. The basic idea of semicolons is to tell the browser that you have just finished a command. You actually don't need them at all if you put each instruction on a different line (with some exceptions discussed below) but it is good practice to put them in anyway.

It is easier to remember where not to put them. Don't put them immediately after a { or } except when creating objects with the {} syntax or functions with the name = function () {} syntax. Don't put them on an otherwise blank line. Do not put two in a row. If you write a command or instruction, you should put a semicolon.

These are examples of where you should put a semicolon:

And these are examples of where you should not put a semicolon:

There are some situations where it is very important to include a semicolon before a line break, even though it may appear to be optional. These occur when the command ends with a variable or function reference, and the first character in the next command is a left parenthesis or square bracket:

var b = 7
var a = 3 + b
(otherwindow?otherwindow.document:document).write('complete')

What will happen is that the JavaScript engine will assume the parenthesis or square bracket relates to the previous command instead of being a new command, and will attempt to run the previous command as a function (or reference its properties in the case of square brackets), and will pass it the result of the expression inside the parentheses as a parameter to it. That makes it equivalent to this:

var b = 7
var a = 3 + b(otherwindow?otherwindow.document:document).write('complete')

That could also be expressed as this:

var b = 7
var foo = otherwindow?otherwindow.document:document
var bar = b(foo)
var a = 3 + bar.write('complete')

In this case, it will produce errors, but there are some cases where it can, in fact, produce a real output, that is meaningless, and the problem is very hard to track down. There are also many other less obvious situations where the same effect can occur without even appearing to be a variable reference on the preceding command (such as where the previous command was assigning an anonymous function to a variable). For this reason, it is useful to always use semicolons when they are optional, as it removes the ambiguity:

var b = 7;
var a = 3 + b;
(otherwindow?otherwindow.document:document).write('complete');

The basic way the script interpreter works is that if there is no semicolon, it will continue scanning the next line, and if it sees something at the start of it that cannot possibly be a part of the command on the previous line, then it will assume you forgot to insert a semicolon, and it will effectively put one there for you. If it can be part of the last command, it will assume it is part of the last command. Operators and property references are an obvious example of what will be allowed to continue the last command.

This can sometimes be used to your advantage, to keep things neat and readable. However, it can also trip you up if you are unprepared for it, as shown above. As an example of this being taken to the extreme, take the following code, which uses no semicolons at all, and lets the script interpreter attempt to work out where the commands start and end:

<p>I</p>
<script type="text/javascript">
var
myP
=
document
.
//comment
getElementsByTagName

/* another
comment */
(
'p'
)
[
0
]
.
innerHTML
+
' was '
alert
(
myP
+
[
'here'
,
'there'
]
[
0
]
)
</script>

Functionally, that is identical to this:

<p>I</p>
<script type="text/javascript">
var myP = document.getElementsByTagName('p')[0].innerHTML + ' was ';
alert( myP + ['here','there'][0] );
</script>

The JavaScript object

This page gives a list of objects, properties, collections and methods for documents and JavaScript components. This view of all properties available from the respective element types is referred to as the JavaScript object.

Some browsers may provide aditional properties or methods but as this stage of the tutorial is intended to teach you how to program so that your scripts work in all the possible (so called 4th generation) browsers, I have given those that are available in all possible browsers or those which have an alternative, which I will also have given. This is the last chapter of this tutorial where I will deal with such a wide range of browsers. In the next few chapters, I will abandon 4th generation browsers, and move on to the much more advanced DOM browsers (also known as 5th generation browsers).

Contents

Key

For items written in the format: 'methodName([someOtherStuff])', someOtherStuff is optional and does not need to be written. The [] brackets must be removed. For items written in the format 'methodName(type varName)', 'type' gives the type of variable expected by the method or collection, called varName in this case.

To use this structure to refer to any object, property, collection or method, treat each new branch level as a new child level. For example:

window.document.nameOfForm.nameOfInput.defaultValue = 'I love it';

Note, all collections also have the length attribute. Also note, window is often omitted when referring to child objects.

Standard document components

Positioned elements

In DOM and proprietary DOM browsers, this could actually be any element, not just one that is positioned. However, at this stage of the tutorial, only 4th generation DHTML is being discussed, and therefore this is referred to as a positioned element. The next chapter will show how this applies to all elements, not just those that are positioned.

Event objects

Intrinsic objects showing constructors

The constructors are all properties of the window object but they are almost always used without 'window.'. Syntax:

var variableName = new intrinsicObjectConstructor(options);

For example:

var myArray = new Array('here','there','everywhere');

Array

Boolean

Date

Note, a UNIX timestamp (milli) is the number of milliseconds since 00:00:00.000 01/01/1970.

Note also that UTC is the time as it would be in the GMT timezone, so GMT and UTC are equivalent.

Function

Image

Number

Object

Option

Regular Expression

String

W3C DOM introduction

This part of the tutorial steps beyond the limits of the older browsers, and will cover the much more advanced abilities of the current browsers.

Thanks to PP:K for linking to this page from his Digital Web article about the W3C DOM. He refers to this as a 'less technical introduction' to the W3C DOM. I am happy with that description, not only because the 'technical introduction' is a 239 page PDF of incredible detail, but because I don't see why the DOM should be that technical. It is designed to be intuitive and easy to use, so that is how I will teach it. Of course there are some incredibly advanced and complex things you can do with the DOM, but with web pages, there is almost never the need to do them. So, instead of going into 239 pages of information on how to convert the planet Earth into a DOM tree, I will give you a gentle introduction to the fundamentals of the DOM, and show you how to achieve the results you need, with the minimum of fuss.

The W3C DOM, commonly known as 'The DOM' or the 'DOM level 1(/2/3)', provides a more realistic and more versatile way of looking at the structure of HTML documents (as well as XML and other document formats). It views HTML documents as a tree structure of elements and text embedded within other elements. All HTML elements, as well as the text they contain and their attributes can be referenced by walking through the DOM tree, their contents can be modified or deleted, and new elements can be created for subsequent insertion into the DOM tree. HTML elements, the text they contain, and their attributes are all known as nodes.

The DOM is designed to interact with more programming languages than just JavaScript, but as this is a JavaScript tutorial (in case you didn't notice...), and most browsers only support JavaScript, I will only describe how to use JavaScript to interact with it.

The DOM is the present and future of browsers, and no doubt it will become the only supported technique for both basic and advanced DHTML in the future.

The W3C DOM is supported by 5th generation browsers. These currently include:

For more details of these browsers, and to download them, please see my list of '4th+ generation' browsers.

This list makes it clear that Internet Explorer 4, Netscape 4, Escape 4, Opera 4, 5 and 6, Omniweb 4.2- and WebTV all do not support the W3C DOM. These browsers are no longer in common use, and can be safely ignored. You may want to check that a browser supports the W3C DOM before you attempt to use it. Note that Opera 4-6 also supports many of the basic functions of the W3C DOM, but cannot actually use most of them. iCab 2- cannot perform advanced W3C DOM functions. Therefore, the best test for W3C DOM support that I can find is this:

if( document.getElementById && document.childNodes && document.createElement ) {
  //do something that requires it
}

Of all the current major browsers, Internet Explorer's DOM support is by far the worst of all. IE 8- for Windows supports half of the DOM 1 modules, and parts of a few DOM 2 modules. The Mac version supports slightly more of DOM 1. IE 9+ supports most of DOM 1 and 2, and a few basic DOM 3 modules, but only if the document triggers standards rendering mode. In quirks mode, IE 9+ reverts to its crippled old version of DOM, and since many scripts will need to work in both modes, they will need to allow for this, and restrict themselves to using only the parts IE supports, or has alternatives for.

Opera, Mozilla/Firefox and Safari/Chrome are competing for first place, with complete DOM 1 support, complete or almost complete DOM 2 support, and a small amount of DOM 3 support. iCab 3 has partial DOM 1 and DOM 2. Opera 9+ is the first and currently only browser to support all DOM 1 and 2 features well enough to claim support for all of them (as well as a number of DOM 3 modules). Safari/Chrome also claims support for all DOM 1 and 2 features, but its DOM 2 Style Sheets and CSS implementation is so bad it should not be allowed to claim support for them at all.

This tutorial will concentrate on things that will work in all these browsers, since they are the most common browsers currently in use, and will show how to bridge the gap to allow Internet Explorer's non-standard features to be used as a partial replacement for missing parts of important DOM features.

ICEbrowser has largely complete DOM 1 and partial DOM 2 (it also claims some DOM 3, but my tests cannot support its claims, especially since it is missing large parts of DOM 2 that it claims to support). It will cope with most of the things I will cover in this tutorial.

Escape 5 can handle enough DOM to reference elements and their styles, and can move them through the document. However, as soon as you try anything more than that (walking the DOM tree, creating new elements, changing their contents, checking for the existence of a childNodes collection, etc.), it produces script errors, and fails to use try...catch correctly, so the errors cannot be avoided. In this state, it is unusable, and it can't even handle many basic DHTML scripts either. Just ignore it. It is hardly used by anyone, and has been abandoned by the vendor.

NetFront has recently added DOM support and it is still very immature. Generally versions earlier than 3.4 will produce errors or crash, and as a result, should be ignored as DOM browsers. Version 3.4 usually performs better.

PP:K maintains many DOM 1 tutorials as well as the ever helpful DOM compatibility tables. Although the DOM provides many more methods, properties and collections than I have shown here, most are not reliable enough to use, so I will only talk you through those that are. If you intend to use other properties or methods, you should check if the browser supports them first using:

if( node.propertyCollectionOrMethodName ) {
  //do something that requires it
}

DOM nodes and tree

The DOM tree

The following text is a snippet of HTML taken from a regular HTML document.

<p title="The test paragraph">This is a sample of some <b>HTML you might<br>have</b> in your document</p>

In your browser, this renders as this (hold your mouse over the paragraph to see the title - most browsers display it as a tooltip, some display it in the status bar):

This is a sample of some HTML you might
have
in your document

The DOM tree views this (simplified) as follows:

                                              P
                               _______________|______________
                              |                              |
                          childNodes                    attributes
                ______________|___________                   |
               |              |           |            title = 'The test paragraph'
'This is a sample of some '   B   ' in your document'
                              |
                          childNodes
                    __________|_______
                   |          |       |
           'HTML you might'   BR   'have'

Of course, the tree also extends above the 'P' from window.document, through the HTML element, down through the body element, then through any other container elements to the paragraph.

The parts of the DOM tree are known as nodes. The 'P', 'B' and 'BR' nodes are element nodes, childNodes and attributes are collections, the title='The test paragraph' pair is an attribute node, and the text strings are text nodes.

Referencing the element nodes

Using the DOM, there are several ways that we could reference the paragraph. We can use getElementsByTagName to reference all paragraphs, then choose the one we want. If the paragraph were to be given an id, we could also use getElementById:

document.getElementById('id of paragraph')
document.getElementsByTagName('p')[indexOfParagraph]

If we assigned the paragraph an id so that we could use getElementById, the id='elementID' pair would appear in the attributes collection, along side title='The test paragraph' in the tree diagram above. Note that if the document is served with an XML based content-type header, getElementsByTagName becomes case sensitive.

NOTE: getElementsByTagName does not return a true collection, it returns an object with element index and 'length' properties. This object keeps itself up to date, so if an element it references is deleted or added, it will automatically change its item references to reflect that change.

We could even walk the entire DOM tree from the document object, for example:

window.document.childNodes[0].childNodes[1].childNodes[4]

In this case, window.document.childNodes[0] should be the HTML element, as it is the first tag in the document (assuming there is no doctype tag), and window.document.childNodes[0].childNodes[1] should be the body tag, as the head element will be the first child of the HTML element. Alternatively, there is a shortcut to the HTML element: document.documentElement so we could use this:

window.document.documentElement.childNodes[1].childNodes[4]

There is also a shortcut to the BODY element: document.body so we could use this:

window.document.body.childNodes[4]

Those last three examples are based on a simple page structure, where the paragraph is a direct child of the body element. Neither of these would be correct in the current document as the document structure is far more complex, also using DIV elements as parents of the paragraph.

The techniques used in those examples can be unreliable. Most browsers will correctly view the blank space between tags as a text node containing only white space characters (such as space, line-break or tab), even if the blank space is not rendered, such as a gap in between a <tr> tag and a <td> tag or a blank gap in between <p> tags. However, some browsers (mainly Internet Explorer 8-, and 9+ in quirks mode) will not view this empty space as a text node at all.

This means that the childNodes collection will be different lengths in these different browsers. If you are trying to walk the DOM tree to the next element node, for example, it may be worth checking each entry in the childNodes collection to see if its nodeType is 1, or to use node.getElementsByTagName.

Because of this, and the fact that the structure of the DOM tree is designed to change as elements are moved, added or removed, the only reliable way to reference an element is using its ID:

var theParagraph = document.getElementById('id of element')

The first entry of the childNodes collection can be accessed using the shortcut firstChild, and the last can be accessed using lastChild. node.nextSibling references the next entry in the parent node's childNodes collection and node.previousSibling references the previous one. To reference the parent node, we use node.parentNode. Note also that all element nodes have the getElementsByTagName method to help reference elements within them. This means that from any node, it is possible to reference any of the other notes around it.

Referencing the attribute node

To reference the title='The test paragraph' attribute pair, we use the attributes collection. Depending on the browser, this collection may be filled up in a variety of different ways, and many empty attribute pairs may exist in the collection. To find the correct attribute, we search through the attributes collection for an attribute whose nodeName matches what we want. The nodeName may be in any case in HTML documents (typically upper case) and should be case sensitive in XHTML and XML if served using an XML based MIME type.

for( var x = 0; x < theParagraph.attributes.length; x++ ) {
  if( theParagraph.attributes[x].nodeName.toLowerCase() == 'title' ) {
    window.alert( 'The value of the \'title\' attribute is: ' +
      theParagraph.attributes[x].nodeValue );
  }
}

Test it here: get the attribute value.

An easy way to check the attribute node

If all you want to do is to check the value of an attribute, not manually edit its entry, it is easier to just use getAttribute.

window.alert( 'The value of the \'title\' attribute is: ' +
  theParagraph.getAttribute('title') );

Attribute names are case sensitive. For example, bgcolor must be written as bgColor.

Test it here: get the attribute value.

Note that according to the specification, getAttribute should always return a string. However, this makes it impossible to differentiate between empty attributes and unspecified attributes. For this reason, browsers will return null for unspecified attributes, even though this is wrong. Opera 7-8 returns an empty string - this was changed to null in Opera 9. As a result, code that checks for attributes and incorrectly tests against null will fail in Opera 7 and 8, because a string will never equate to null. There is no need to test against null, just check if get attribute failed to retrieve a value:

if(!element.getAttribute('attribute_name'))

Changing the attribute

The attributes of an element can be set or changed using setAttribute:

element.setAttribute('attributeName','attributeValue')
theParagraph.setAttribute('align','center')

Attribute names are case sensitive. For example, bgcolor must be written as bgColor.

You can also remove attributes, with a few exceptions, using removeAttribute:

theParagraph.removeAttribute('attributeName')

Move the paragraph here so you can see what you are doing then:
Change the title attribute and Change it back (hold your mouse over the paragraph to see if the title has changed).

Reading and writing problematic attributes

Internet Explorer 7- (and some minor browsers) cannot set values for style, class or event handlers, using setAttribute. Internet Explorer 8 has fixed most of these, but still cannot set event handlers. Internet Explorer 9 can now set these attributes in standards mode. A few more browsers also have trouble reading these attributes using getAttribute. Internet Explorer generally returns the wrong type of result, such as an object instead of a string, when using getAttribute for these attributes. The DOM does provide a few alternatives to allow the functionality to be approximated in these browsers.

The class is available as a read/write string called className - this is discussed in the DOM CSS chapter of this tutorial.

The event handler attributes are available as referenced functions (this is not the case for handlers added using DOM events), with their names matching the attribute name; element.onclick. These are read/write but must be written as a reference to a function, not as a direct string. They can also be written as a string using the Function constructor:

element.onclick = new Function(codeAsAString);

They may also be read as a string using the toString method of the function, but note that it will normally contain the anonymous function wrapper, and may not be available at all in browsers running on devices with limited capabilities, such as mobile phones. Note also that it will not be available at all if the attribute is not present:

var functioncode = element.onclick.toString();

The string value of the style attribute is available as a read/write string called cssText, which is a property of the style object, which itself is a property of the element. Note, however, that it is not supported very well; Safari does not support it up to version 1.1 (reading it produced the value null), Mozilla versions prior to 1.0 could not write to it, and iCab 3-, NetFront and Escape/Evo do not support it at all. To avoid problems with its use, a combination of cssText and getAttribute/setAttribute can be used. To read it:

var cssString;
cssString = element.style.cssText;
if( typeof(cssString) != 'string' ) {
  cssString = element.getAttribute('style');
}

To write it, simply set both versions, and the browser will use whichever one works:

var cssString = 'color:lime;font-weight:bold;';
element.style.cssText = cssString;
element.setAttribute('style',cssString);

Note that this will then prevent it from being read correctly if other styles are changed individually. If this will cause a problem, check if cssText is supported first:

var cssString = 'color:lime;font-weight:bold;';
if( typeof(element.style.cssText) == 'string' ) {
  element.style.cssText = cssString;
}
element.setAttribute('style',cssString);

Referencing the text nodes

To give a full example, I will try to reference the text node 'HTML you might'. To do this, I will go through the second entry of the childNodes array of the 'P'. This will be a reference to the 'B'. I will then look at the firstChild (equivalent to the first entry in the childNodes collection) of the 'B' to reference the text node.

window.alert( 'The value of the text node is:\n' +
  theParagraph.childNodes[1].firstChild.nodeValue );

Test it here: get the value of the text node.

Also important to note is that although the specifications say that no matter how much text exists between tags, it should all be in one text node, in practice this is not always the case. In Opera 7-9.2x and Mozilla/Netscape 6+, if the text is larger than a specific maximum size, it is split into multiple text nodes. These text nodes will be next to each other in the childNodes collection of the parent element.

In Opera 7-9.2x, this maximum text node size is 32 KB. In Mozilla/Firefox/Netscape 6+, it is 4 KB. Although the normalize() method of the parent node(s) should be able to replace the multiple text nodes with a single text node containing all the text, this only works in Mozilla/Firefox/Netscape 6+. In Opera 7-9.2x it puts all of the text into a single node and then truncates that node to 32 KB, so the contents of all except the first node are lost. Running the normalize method can crash Internet Explorer 6 and does not exist in Internet Explorer 5 on Windows.

For this reason, I do not recommend trying to normalize. It is better to manipulate the contents of text nodes separately. In fact, you can create your own text nodes and add them to the childNodes collection. Although to the DOM, they will still appear as separate nodes, they will appear as a single piece of text in the document. Basically, you need to be aware that your text may be split into several nodes, if it is 4 KB or over, or if you have added extra text nodes in yourself. In order to get that text in a single variable, you may need to look through every child node, and if they are consecutive text nodes append them together to get the total string.

Changing the text of text nodes

Once you have a reference to the text node, you can read or write its contents using its nodeValue.

theParagraph.childNodes[1].lastChild.nodeValue = 'want to change';

Move the paragraph here so you can see what you are doing then:
Change the text node | Change it back.

Creating new nodes and removing existing ones

This is what the DOM was truly created for. In order to create new nodes, we use a couple of methods of the document object to create the node. We then insert the node into the main DOM tree, at which point the browser will display it. We can also move existing nodes (like the test paragraph) simply by inserting them into the DOM tree somewhere else.

Note that when creating element nodes, the element name must be in lower case. Although in theory it should not be case sensitive with HTML, I have noticed some problems in Konqueror when using upper case with strict doctypes - see the top of this page. It will be case sensitive with XHTML (in all compliant browsers, not just Konqueror), and must be in lower case.

var theNewParagraph = document.createElement('p');
var theTextOfTheParagraph = document.createTextNode('Some content.');
theNewParagraph.appendChild(theTextOfTheParagraph);
document.getElementById('someElementId').appendChild(theNewParagraph);

We could also use insertBefore instead of appendChild, or even manually add the new element to the end of the end of the childNodes collection. Using replaceChild, we could also overwrite existing nodes. It is also possible to copy a node using cloneNode(true). This returns a copy of the node but does not automatically add it into the childNodes collection. Using element.removeChild(referenceToChildNode), we can remove existing nodes.

Test it here: create a new paragraph.

How about something even more complicated. What about adding HTML elements within the new element, instead of just plain text. Here, I will recreate the test sentence from above, one piece at a time.

//three elements are required: p, b, br
var theNewParagraph = document.createElement('p');
var theBoldBit = document.createElement('b');
var theBR = document.createElement('br');

//set up theNewParagraph
theNewParagraph.setAttribute('title','The test paragraph');

//prepare the text nodes
var theText1 = document.createTextNode('This is a sample of some ');
var theText2 = document.createTextNode('HTML you might');
var theText3 = document.createTextNode('have');
var theText4 = document.createTextNode(' in your document');

//put together the bold bit
theBoldBit.appendChild(theText2);
theBoldBit.appendChild(theBR);
theBoldBit.appendChild(theText3);

//put together the whole paragraph
theNewParagraph.appendChild(theText1);
theNewParagraph.appendChild(theBoldBit);
theNewParagraph.appendChild(theText4);

//insert it into the document somewhere
document.getElementById('someElementId').appendChild(theNewParagraph);

Test it here: recreate the test paragraph.

In case you were wondering how I managed to make those new paragraphs end up just above the links you clicked on, this is how.

The link you clicked on is in a paragraph. The paragraph is in a div (although this technique would work anywhere). The script is run in the event handler for the link. Therefore, in the handler function, 'this' refers to the link. The parentNode of the link is the paragraph - this.parentNode - and the parentNode of the paragraph is the div - this.parentNode.parentNode. I want to get the div to import the new paragraph node I have created above the paragraph the link is in, so I want to say this:

theDIV.insertBefore(theNewParagraph,theCurrentParagraph);

In JavaScript, this would be:

this.parentNode.parentNode.insertBefore(theNewParagraph,this.parentNode);

As for making them disappear when you click on them, when creating these paragraphs, I also assign an onclick event handler function that uses this.parentNode to reference the div, and then uses removeChild to delete the paragraph:

theNewParagraph.onclick = function () { this.parentNode.removeChild(this); };

Note that nodes belong to the document they were created in. So for example, if your page uses frames, and you create a paragraph node in one frame then attempt to add it to the document in another frame, it will not work. In theory you can use the document.importNode method to create a copy of it in the new document, but that method does not exist in Internet Explorer. If a script in one frame needs to create a node and append it to a document in another frame, it must use the document object for the destination frame when creating the node:

var newP = parent.frames['leftfr'].document.createElement('p');
parent.frames['leftfr'].document.body.appendChild(newP);

Using document fragments

It is also possible to deal with multiple nodes at the same time. Say for example that you want to create 10 paragraphs, and add them all to the document at the same time as each other, instead of one at a time. This can be done using a document fragment. The benefit of using this is that it creates fewer document reflows, and as a result it can improve performance for big changes.

A document fragment is like a normal element (such as a div), except that it cannot become a part of the document itself. If you try to append a document fragment to any part of a document, instead of appending the fragment, the browser will add the child nodes of the fragment. For example, you create 10 paragraphs, append them to a document fragment, then append the document fragment to the body of a document. Instead of appending the fragment to the body, it will add the 10 paragraphs as children of the body.

var frag = document.createDocumentFragment();
for( var i = 0, p; i < 10; i++ ) {
  p = document.createElement('p');
  p.appendChild(document.createTextNode('Paragraph '+(i+1)));
  frag.appendChild(p);
}
document.body.appendChild(frag);

Test it here: create and append a new document fragment.

DOM tables

Assuming that you have a reference to the table element (see the last section), you can do some fairly in-depth manipulation of the table's structure. In order to do this, you have to view tables as in the full HTML4 specification. A table contains a caption, a thead, any number of tbodies, and a tfoot. If you did not specify a tbody, the DOM will still have one tbody in the tBodies collection, which will contain all of the rows. There are many methods associated with these. Most of those designed to create parts of tables, as well as the one to delete table captions, are not implemented properly in some older browsers, so I will concentrate on those that are the most reliable.

As well as being able to walk through the DOM tree as before, you can also walk through the DOM table tree. Each table has four extra properties that reference the various childNodes:

caption
References the caption of the table
thead
References the thead of the table, if there is one
tfoot
References the tfoot of the table, if there is one
tbodies
A collection with one entry for each tbody (usually just table.tbodies[0])

Each thead, tbody and tfoot also has a rows collection with an entry for each row in that thead, tbody or tfoot. Each row has a cells collection containing every td or th cell in that row. Each cell then contains the usual DOM references to its contents.

Each table also has the createTHead() and createTFoot() methods to create or return the thead or tfoot respectively, as well as the deleteTHead() and deleteTFoot() methods to delete them. There are also the createCaption() and deleteCaption() methods to create/return and delete the caption. These methods do not work correctly in Internet Explorer on Mac. Adding tbodies can only be done with basic DOM methods - there is no dedicated method.

Adding a row to a table

The DOM provides dedicated methods for creating and adding rows, but these fail in Internet Explorer on Mac. It is easier to just use normal DOM methods, since these work in everything:

var theTable = document.getElementById('tableId');
theTable.tBodies[0].appendChild(document.createElement('tr'));

Rows have the rowIndex property, giving their index within their table, and the sectionRowIndex property, giving their index within their thead/tfoot/tbody.

The thead, tfoot and tbodies each have the dedicated method insertRow which can be used to add rows in all current browsers. This accepts a parameter; the 0-based index of where to add the new row (or -1 to append it at the end). They have the deleteRow method to delete them, which accepts the same index parameter, with -1 deleting the last row. These methods do not work correctly in Internet Explorer on Mac.

Adding one cell to every row in a table

This example adds a new cell on the end of every row in a table. It assumes that table has both a thead, and a tfoot. Again, there are dedicated methods for this, but these fail in Internet Explorer on Mac, so they are not used here:

var theTable = document.getElementById('tableId');
for( var x = 0; x < theTable.tHead.rows.length; x++ ) {
  var y = document.createElement('td');
  y.appendChild(document.createTextNode('Thead cell text'));
  theTable.tHead.rows[x].appendChild(y);
}
for( var z = 0; z < theTable.tBodies.length; z++ ) {
  for( var x = 0; x < theTable.tBodies[z].rows.length; x++ ) {
    var y = document.createElement('td');
    y.appendChild(document.createTextNode('Tbody cell text'));
    theTable.tBodies[z].rows[x].appendChild(y);
  }
}
for( var x = 0; x < theTable.tFoot.rows.length; x++ ) {
  var y = document.createElement('td');
  y.appendChild(document.createTextNode('Tfoot cell text'));
  theTable.tFoot.rows[x].appendChild(y);
}

Cells have the cellIndex property (except in early Konqueror versions), giving their index within their row.

Rows have the dedicated method insertCell which can be used to add cells in all current browsers. This accepts a parameter; the 0-based index of where to add the new row (or -1 to append it at the end). The added cell will always be a td, not a th. Rows have the deleteCell method to delete them, which accepts the same index parameter, with -1 deleting the last row. These methods do not work correctly in Internet Explorer on Mac.

DOM CSS

Individual element styles

As described in the section on DHTML, DOM compatible browsers support the style object. The style object was created by Microsoft and although only became a standard in DOM Level 2 CSS, there was nothing to replace it in browsers supporting earlier DOM implementations, so it has been universally adopted by all DOM browsers. Through the style object, DOM browsers allow all styles the element accepts to be changed, not just those I described in the section on DHTML. It is equivalent to reading and writing the value of the style attribute, so styles can only be read if they were set using that attribute. Styles are almost all read/write.

The syntax is simple. Simply write element.style.styleName. If the style name consists of two or more words separated by hyphens '-', remove the hyphen and capitalise the first letter of the word following it. For example, background-color becomes backgroundColor. When setting a value, it should be set as a string. Other data types will be converted to strings before setting the value to that string. Although most browsers will allow you to delete a style by setting it to null, a browser could legitimately set it to the string value 'null', and that would have an unwanted effect. If you want to delete a style, set it to an empty string, and never set it to null.

The only odd style is float, which is a reserved word in many languages, including JavaScript. As a result, the float style must be set using cssFloat in standards compliant browsers, and styleFloat in Internet Explorer 8- and 9+ in quirks mode (some standards compliant browsers also provide this as well). Simply set both of them. It will not cause any problems, and will work in all possible browsers.

The W3C DOM does provide an alternative way to change the styles on individual elements. The class attribute of the element can be removed using this:

element.className = '';

And it can be changed using:

element.className = 'newClassName';

Reading applied element styles

When CSS stylesheets are applied to a page, elements may be targetted by several style rules, and inherit various styles from their ancestors. They will also receive some default styling from the browser's internal stylesheet. The DOM specification offers a way to work out what the result of those styles was, using the window.getComputedStyle method. This is supported by Opera 7+, Mozilla, iCab 3+, Konqueror 3.5+, and Chrome/Safari 3+, as well as Internet Explorer 9+ in standards mode. Internet Explorer also offers an alternative that also works in quirks mode; the currentStyle property of an element. This is supported by Internet Explorer on Windows and Mac, Opera 9+ and iCab 3.

getComputedStyle requires two parameters. The first is a reference to the element. The second is either the name of a pseudo element (':before', ':after', ':first-line'), or null just for the element itself. Since currentStyle only allows you to obtain the style for the element itself, that is all I will show here. In most cases, the values returned by these are the same, but there are a few cases where they may be different. For example, a table may have a width set by the CSS, but may be stretched wider to fit its contents. In this case currentStyle will still return the width specified by the CSS, but getComputedStyle will return the new stretched width.

var oElement = document.getElementById('mydiv'), oColor;
if( window.getComputedStyle ) {
  oColor = window.getComputedStyle(oElement,null).color;
} else if( oElement.currentStyle ) {
  oColor = oElement.currentStyle.color;
}

As well as being an actual usable value, the returned value for several styles may be their default values, such as 'auto', 'normal', or 'inherit'. The browser may also choose to return values in any available unit. For example, even if the CSS specifies a length in 'em' units, the browser may still return the equivalent value in 'px' units.

In cases where currentStyle gives a different result to getComputedStyle, Internet Explorer offers an alternative; runtimeStyle. This is an object almost exactly like currentStyle, but it gives a computed value instead of a cascaded value. However, it does not always give a usable value, and often gives a different kind of result to what would be expected from getComputedStyle, if it gives any value at all. It often computes a blank string for unspecified values, instead of their default values. Use this with care and only if you are sure it gives the result you need.

It is also possible to obtain some details of an element using its offset, client, and scroll values. These are all available as properties of the element, and give its current dimensions in pixels, not the styles that apply to it. This means that they also give real dimensions, even if the CSS value is 'auto'. These are not reliable for inline elements, and should only be used for elements that have block or equivalent display. Note that these properties were not standardised for a long time (they will be part of future DOM standards), but they work fairly reliably cross browser.

offsetWidth and offsetHeight
The dimensions of the element taken outside its border. (The width inside the padding in IE's quirks mode.)
clientWidth and clientHeight
The dimensions of the element taken inside its border, and minus any scrollbar size.
scrollWidth and scrollHeight
The dimensions the element would be using if it did not have a scrollbar and was not constrained in size (in other words, the dimensions of its contents, plus its padding). Only usable and reliable if the element actually has a scrollbar.
scrollLeft and scrollTop
The distance the element has been scrolled.

Rewriting stylesheets

The DOM allows the stylesheets themselves to be changed. Some browsers allow stylesheets to be modified, some also allow rules to be created or removed. This will be covered in the next chapter of this tutorial.

Changing the href of a linked stylesheet

The href of the stylesheet can be read in some browsers using the href property. Opera 7+, Mozilla, Konqueror, Safari/Chrome and Internet Explorer 5+ can set the href of the link element using setAttribute on the link tag, and Internet Explorer 4+ can also set the href of the styleSheets[index].

Switching stylesheets

The stylesheet can be disabled/enabled in some browsers using the boolean disabled property, but some browsers will only allow you to change the disabled property if the title attribute is set. To create the most reliable cross browser effect, the function I have written (below) will only try to enable / disable a stylesheet if its title attribute has been set.

The best use of the disabled property is to allow your readers to change the look of the site to suit their personal tastes or accessibility requirements. This requires them to be able to switch between the different stylesheets that you provide. Most often, this technique is used along with cookies, storing the user's preference of stylesheet as they leave the page (via the onunload event) and then using it again next time they view the page (using the onload event).

Setting up the HTML

To do this, set out your document as follows:
Firstly, if you need one, create a persistent stylesheet with all global styles, using this:

<link rel="stylesheet" type="text/css" href="all.css">

As it has no title attribute, it will never be disabled. You do not have to define this stylesheet if you do not want to, but if you do, remember that all styles you define in it will be used in all stylesheet modes, and will be used along with any stylesheet you later use by stylesheet switching.

The default switchable (preferred) stylesheet is set up using this:

<link rel="stylesheet" type="text/css" href="default.css" title="Default">

This will also be used by browsers that cannot disable/enable stylesheets and ensures that the default view will look good. You can then set up all alternative stylesheets using this:

<link rel="alternate stylesheet" type="text/css" href="extraPretty.css" title="Pretty">
<link rel="alternate stylesheet" type="text/css" href="bigFont.css" title="Big Font">
<link rel="alternate stylesheet" type="text/css" href="contrast.css" title="High Contrast">

Because these stylesheets are set up using 'alternate stylesheet' for the 'rel' attribute, lesser browsers will not use these stylesheets at all, and in browsers that can switch stylesheets, these stylesheets will be disabled by default.

Changing the stylesheets with the browsers' view menu

Opera 7+, Mozilla, Internet Explorer 8+, Konqueror 3+ and iCab 3+ will allow users to choose from these alternative stylesheets in the 'view' menu. However, this setting is not remembered by the browser (except by Konqueror), so the user will have to choose this for every page they view on your site. Also, other 'good' browsers, like Internet Explorer 7- and Safari/Chrome do not allow the user to choose stylesheets in any menus, so you will have to use the DOM to change them manually. Note that when using the view menu or the DOM technique, stylesheets that share the same title will be treated as if they were the same stylesheet and will be switched together.

Using the view menu only allows you to select stylesheets whose titles are identical. If you want to enable combinations, you would either have to produce combined stylesheets or include the same sheet multiple times with different titles to allow all combinations. This is also a problem with most basic stylesheet switching scripts. The script I will show you allows you to choose a combination of stylesheet titles, making it superior to most other scripts.

Referencing the stylesheet's disabled property and title property

document.stylesheets

Microsoft first came up with the document.styleSheets collection. The W3C decided this was a good idea and introduced it in DOM Level 2 CSS. All stylesheets are available in this collection, even if the rel attribute is set to 'alternate stylesheet'. Setting the disabled property of the collection entry disables and enables the stylesheet, and the title attribute is available here as a JavaScript property.

Referencing the tags

The equivalent to document.styleSheets can be produced by referencing the relevant LINK and STYLE tags. The disabled property can be read and modified, and the title attribute is available. You will need to make sure that the link tags are being used to import stylesheets by checking their 'rel' attribute as link tags have many uses.

You can see that from these points, the following is required:

The W3C says the correct method is to use the styleSheets collection, but since that would only work in three browsers, and cause problems for others, I will use the link and style tag technique. I will add support for ICEbrowser, just in case they ever fix its handling, or in case you only want to switch preferred stylesheets.

The best way to check if the user has used the view menu is to check what stylesheets are enabled onload and onunload. If they are different, the user has chosen something from their view menu (this is not possible in Opera 7-8, as the disabled property does not reflect changes made in the view menu). Of course, you should also check if the changeStyle function has been run by making it store a variable to say so.

You may want to use the correct technique, but if you do, you will still need to do the link and style tag technique for Opera 7-8 and iCab (using if(!document.styleSheets) etc.), and you will need to do a browser detect to make KHTML/WebKit use the link and style tag technique, and you will still need to make sure you do not store the preference in KHTML/WebKit, iCab and Opera 7-8 until the user has chosen, and you will need to compensate for the lack of title in IE 4 Mac.

The script

Note, because getElementsByTagName returns an object with element 'index' and 'length' properties, and not a true collection, we cannot use the array.concat method to append the 'arrays' of link tags and style tags. Instead, we must cycle through the collections and add each element in turn.

function getAllSheets() {
  //if you want ICEbrowser's limited support, do it this way
  if( !window.ScriptEngine && navigator.__ice_version ) {
  	//IE errors if it sees navigator.__ice_version when a window is closing
  	//window.ScriptEngine hides it from that
    return document.styleSheets; }
  if( document.getElementsByTagName ) {
    //DOM browsers - get link and style tags
    var Lt = document.getElementsByTagName('link');
    var St = document.getElementsByTagName('style');
  } else if( document.styleSheets && document.all ) {
    //not all browsers that supply document.all supply document.all.tags
    //but those that do and can switch stylesheets will also provide
    //document.styleSheets (checking for document.all.tags produces errors
    //in IE [WHY?!], even though it does actually support it)
    var Lt = document.all.tags('LINK'), St = document.all.tags('STYLE');
  } else { return []; } //lesser browser - return a blank array
  //for all link tags ...
  for( var x = 0, os = []; Lt[x]; x++ ) {
    //check for the rel attribute to see if it contains 'style'
    if( Lt[x].rel ) { var rel = Lt[x].rel;
    } else if( Lt[x].getAttribute ) { var rel = Lt[x].getAttribute('rel');
    } else { var rel = ''; }
    if( typeof( rel ) == 'string' && rel.toLowerCase().indexOf('style') + 1 ) {
      //fill os with linked stylesheets
      os[os.length] = Lt[x];
    }
  }
  //include all style tags too and return the array
  for( var x = 0; St[x]; x++ ) { os[os.length] = St[x]; } return os;
}
function changeStyle() {
  for( var x = 0, ss = getAllSheets(); ss[x]; x++ ) {
    //for each stylesheet ...
    if( ss[x].title ) {
      //disable the stylesheet if it is switchable
      ss[x].disabled = true;
    }
    for( var y = 0; y < arguments.length; y++ ) {
      //check each title ...
      if( ss[x].title == arguments[y] ) {
        //and re-enable the stylesheet if it has a chosen title
        ss[x].disabled = false;
      }
    }
  }
  if( !ss.length ) { alert( 'Your browser cannot change stylesheets' ); }
}

...

eg.
<body onload="changeStyle('High Contrast','Big Font');">

Reduced, that becomes this:

function getAllSheets() {
  if( !window.ScriptEngine && navigator.__ice_version ) { return document.styleSheets; }
  if( document.getElementsByTagName ) { var Lt = document.getElementsByTagName('link'), St = document.getElementsByTagName('style');
  } else if( document.styleSheets && document.all ) { var Lt = document.all.tags('LINK'), St = document.all.tags('STYLE');
  } else { return []; } for( var x = 0, os = []; Lt[x]; x++ ) {
    var rel = Lt[x].rel ? Lt[x].rel : Lt[x].getAttribute ? Lt[x].getAttribute('rel') : '';
    if( typeof( rel ) == 'string' && rel.toLowerCase().indexOf('style') + 1 ) { os[os.length] = Lt[x]; }
  } for( var x = 0; St[x]; x++ ) { os[os.length] = St[x]; } return os;
}
function changeStyle() {
  for( var x = 0, ss = getAllSheets(); ss[x]; x++ ) {
    if( ss[x].title ) { ss[x].disabled = true; }
    for( var y = 0; y < arguments.length; y++ ) {
     if( ss[x].title == arguments[y] ) { ss[x].disabled = false; }
} } }

Test it using the stylesheet switcher demo page.

See my stylesheet switching header file for a working example that can store the user's choice of stylesheet, even if they use the view menu in Gecko.

DOM Style Sheets

DOM Style Sheets allow you to step through the rules of each stylesheet, change the selectors, read and write styles, and add new rules. This allows you to create or change CSS that affects several elements at the same time, instead of just one element as with traditional DHTML. It allows you to take advantage of CSS selectors to target the desired elements, and enter rules into the CSS cascade.

Currently this is supported according to the DOM standard by Opera 9+, Mozilla/Firefox and Internet Explorer 9+ (in standards mode). It is partially supported by Safari, Chrome, Konqueror and ICEbrowser, but their support is so bad it is largely unusable. I will give details of what fails in these browsers for each operation. Supposedly you can use the hasFeature method to check if a browser supports DOM 2 Style Sheets well enough, but unfortunately Safari/Chrome and ICEbrowser return true for the StyleSheets and CSS features. Their support is so poor that this is an insult to the spec. I suggest you ignore Safari/Chrome, Konqueror and ICEbrowser for now, and hope that their support improves in the future.

If you need compatibility with Safari/Chrome, Konqueror, ICEbrowser, or any browser that does not support DOM Style Sheets (like iCab or Opera 8 or below), then you should ensure that your script does not requre document.styleSheets to be supported, and that if it uses it, it does not produce errors when something fails in one of these browsers.

Internet Explorer 8- does not comply with the standard here, so if you are testing, use a browser that supports the standard correctly, such as Opera 9+ or Mozilla. I will give details later on how to get support for Internet Explorer 8- as well. Internet Explorer 9+ only adds support for DOM Style Sheets in standards mode. In quirks mode or IE 7 compatibility mode, only the Internet Explorer model is provided, along with its limitations. Internet Explorer on Mac supports large parts of the Internet Explorer model, but it also provides the cssRules collection, even though it does not use it according to the spec. I recommend you ignore IE Mac, but I will include it in the Internet Explorer section later on in this chapter.

DOM stylesheets does not provide an exact copy of what you put in your stylesheet. It produces what the browser sees, and what it interprets. Rules that it does not understand are not included, whitespace may be added or removed, combined styles may be split into their components, and split styles may be combined. Styles that are not understood will be ignored. Comments will not be included. Do not expect to be able to recognise your own stylesheet.

Note that you should not try using DOM 2 Style Sheets until you are certain that the stylesheet has completed loading (typically this means waiting for the document's onload event to fire). If you attempt to access it before then, the cssRules collection may be too short or may not exist.

The styleSheets list

All stylesheets are available through the document.styleSheets collection. This includes stylesheets added using <style> and <link> tags, including persistent, preferred and alternate stylesheets, no matter if they are enabled or not. Even stylesheets added using style sheet processing instructions in XML based documents should be included. They are available in the collection in the order that they appear in the document source. Imported stylesheets are not included in the list (as they are included through their parent stylesheet), and will be dealt with later. The collection does not include the contents of <div style="etc"> attributes; they must be accessed through the style property of the element (see the section on DHTML for more details).

This provides an easy way to check if a browser supports some amount of DOM Style Sheets, by checking for the existence of the document.styleSheets collection:

if( document.styleSheets ) {
  //DOM stylesheets are available
}

Each stylesheet has a number of properties that give details of the stylesheet, such as its URL (href), its title (title), its type (type, usually 'text/css'), the media types it applies to (media), and if it is disabled or not (disabled). The disabled property can be set to true or false to disable or enable the stylesheet, but the other properties are all read-only. The properties will only be available if they actually apply to the stylesheet in question. For example, if a link element does not have the title attribute set, then its associated StyleSheet object will not have a title.

Test it here: see the properties of the first stylesheet in this document.

Relationships between the StyleSheet object and the HTML tags that create them

If you have a reference to the LINK or STYLE element, you can reference the associated StyleSheet object using the sheet property of the element.

var theSheet = document.getElementsByTagName('style')[0].sheet;

Similarly, if you have a reference to the StyleSheet object, you can reference the associated LINK or STYLE element by using the ownerNode property of the StyleSheet object.

var theElement = document.styleSheets[0].ownerNode;

Test it here: check for these properties.

Stylesheet media

The media property of the stylesheet references a MediaList object. This allows adding and removing of media types. It has a property mediaText that gives the full contents of the LINK or STYLE element's media attribute. Note that browsers are free to add or remove spaces, as they decide is appropriate. They may also remove any media types they do not understand, or may rename them (typically to 'unknown'). If the text contains the same media type more than once, the browser may remove the extra copies. It is possible to set this text to a new string if desired:

document.styleSheets[0].media.mediaText = 'print,handheld';

Modifying the media types that the stylesheet applies to is mostly useful if the browser supports multiple media types. Of the browsers that support DOM stylesheets, only Opera supports more than just screen and print (Opera also supports projection, handheld, speech, and on some devices, tv). This means that currently, this feature is most useful in Opera, but can also be useful in other browsers if you wish to change between 'all', 'screen', and 'print'. Note that the browser may support media queries, and the specification never took these into account. Currently this affects Opera. Opera normalizes media queries (removing spaces and reformatting), to make sure that only one copy of each equivalent query (with parameters in the same order) exists.

If you set the mediaText to a value containing media types the browser does not recognise, it will allow that, and simply ignore unknown media types. Setting the media text to a value that the browser does not recognise as valid syntax will throw an error, so browsers that do not understand media queries will not allow you to set mediaText containing a media query. You will need to use a try...catch statement around the assignment.

The media object also provides the appendMedium method to allow you to append one media type at a time, and the deleteMedium method to delete one at a time. Deleting a medium that is not in the list, or that the browser does not understand, will cause it to throw an error. Note that a stylesheet with no media is applied to all media types. Adding a medium to a stylesheet that has no specified media will cause it to be applied only to that media type. Deleting all media types from the list will cause the media text to be empty, so it will be applied to all media types.

document.styleSheets[0].media.appendMedium('handheld');
document.styleSheets[0].media.deleteMedium('print');

You can also step through all the listed media types using the item method, and check how many are in the list by checking the length parameter.

for( var i = 0; i < document.styleSheets[0].media.length; i++ ) {
  alert(document.styleSheets[0].media.item[i]);
}

Test it here:

Stylesheet cssRules collection

Stylesheets all have the cssRules collection, that contains all the rules of the stylesheet. This only includes rules that are in the root of the stylesheet, and not, for example in a @media block (these will be dealt with later). Browsers may choose not to include @ rules that they do not recognise (initially the spec said to include them, but the authors of the spec have now said not to). They will not include any rules that they do not understand, such as rules using a selector that they do not recognise as being in a valid format. This means that the cssRules collection will almost certainly be a different length in different browsers, and you cannot rely on it being a certain size if you use features that are not supported by all the browsers. Note that although most browsers understand @namespace rules, they are not part of the specification. Only Mozilla adds these to the cssRules collection, as an unknown rule.

Rules can be added to the collection using the insertRule method of the stylesheet, specifying the text to interpret as a new rule, and the index specifying the rule number that the new rule should be added before. To add a rule at the end, the index should be the number of rules currently in the stylesheet. Rules can be removed using the deleteRule method of the stylesheet, specifying the index of the rule to remove. As always, the index starts at 0 for the first rule.

document.styleSheets[0].deleteRule(1); //delete the second rule
document.styleSheets[0].insertRule('html { color: lime; }',0); //add a new rule at the start
var oLength = document.styleSheets[0].cssRules.length;
document.styleSheets[0].insertRule('body { background: #779; }',oLength); //add a new rule at the end
var oRule = document.styleSheets[0].cssRules[oLength]; //reference the new rule we just added

This part of DOM 2 Style Sheets is one of the most useful and important. Since adding and removing rules is not supported by Safari 2- and Konqueror, and only removing rules is supported by ICEbrowser, they are generally useless for DOM 2 Style Sheets.

Test it here: add the new rule 'p { color: lime; }', and then delete it again.

For security reasons, Opera and Mozilla will not allow you to access the cssRules collection of a stylesheet from another domain or protocol. Attempting to access it will throw a security violation error. If your script is likely to encounter a stylesheet like this, you should use a try...catch structure to prevent your script from halting.

The CSS rule

This is the most fundamental part of DOM 2 Style Sheets; viewing or changing the styles of each rule. Each entry in the stylesheet's cssRules object is a CSS rule. There are several types of rule that may exist in a stylesheet, and the properties they provide will depend on what type of rule they are.

Each rule has a type property, saying what type of rule it is. This will be 1 for normal style rules, 2 for @charset rules, 3 for @import rules, 4 for @media rules, 5 for @font-face rules, and 6 for @page rules. If the browser does not ignore unknown @ rules, their type will be 0. CSS rules will also have the parentStyleSheet property, which is a reference to the stylesheet they are inside. Typically, you would need to use the type property to work out what other properties you will be able to access.

The rule also has a cssText property that gives a text representation of the rule. This may have whitespace added or removed, and styles may be split or combined into component styles. Multiple copies of the same style may be replaced with a single copy of the one that is actually used. In most cases, it no longer looks like the rule that was in the original stylesheet, but it should have the same effect in that browser (though not necessarily in other browsers) as the original rule.

var theRule = document.styleSheets[0].cssRules[0];
alert('Type: '+theRule.type+'\nRule: '+theRule.cssText);

Test it here: alert the first rule in the first stylesheet of this page.

In theory it is possible to set the cssText to a new value, as long as the new value can be interpreted as the same type of rule as the original rule (so it is not possible to convert a @import rule into a @media rule, for example). Unfortunately, browser support is not good enough to use this at the moment.

document.styleSheets[0].cssRules[0].cssText = 'body { background: #779; }';

If the rule is a @charset rule, it will have an encoding property, giving the encoding string, which can be rewritten if desired. Unknown @ rules will have no other properties. All other rule types will be covered below:

Normal style, @page, and @font-face rules

These are the most common rule types, and the majority of stylesheets contain only normal style rules. The normal style rule and the @page rule have the selectorText property, which gives the selector text for the rule. This may have whitespace added or removed. In theory, this can be rewritten if desired, but unfortunately, browser support is not good enough to use this at the moment.

document.styleSheets[0].cssRules[7].selectorText = 'div > p';

All three rule types also have a style property. This is the same in functionality as the style property of elements that is normally used for DHTML. However, browsers that support DOM 2 Style Sheets usually implement more of the less well known methods associated with the style object. As well as the usual .style.color syntax that is well known (and even works in Safari and Konqueror), there are several other ways to access and modify the styles of the rule:

The cssText property gives the text representation of all the styles that the rule contains. This may be reformatted with altered whitespace, may or may not have a final semicolon, and styles may or may not be split or combined. It is possible to set this to a new value, replacing all the original styles with a new set of styles.

document.styleSheets[0].cssRules[7].style.cssText = 'color: lime; font-weight: bold;';

The length property can be used to find how many styles the browser sees, and the item method can be used to retrieve the style names one at a time. The getPropertyValue method retrieves the value of a named style. The getPropertyPriority method retrieves the priority of a named style (typically 'important'). The removeProperty method removes a named style (it will do nothing if the style is not being used). Lastly, the setProperty method creates or rewrites a style. It requires three parameters; the style name, the value, and the priority.

var oStyle = document.styleSheets[0].cssRules[0].style;
oStyle.setProperty('color','lime','');
oStyle.setProperty('font-weight','bold','important');
for( var i = 0, j, s = ''; i < oStyle.length; i++ ) {
  j = oStyle.item(i);
  s += j + ' = '+oStyle.getPropertyValue(j)+' '+oStyle.getPropertyPriority(j)+'\n';
}
alert(s);
oStyle.removeProperty('color');
oStyle.removeProperty('font-weight');
alert('New content: '+oStyle.cssText);

Test it here: the last rule in the demo stylesheet is currently 'p { }'. Try running the script shown above on it.

@import rules and imported stylesheets

@import rules are similar to a HTML LINK element, in that they reference a new stylesheet. They have some extra properties that reflect that role.

The href property gives the URL referred to by the import rule. This may be a complete address (typically beginning with 'http://'), or may be the exact URL given by the @import rule (such as 'imported.css') - this will depend on the browser.

@import rules can contain a list of media types as well, although this feature is not very often used. As a result, the rule object also has the media property, which behaves exactly like the media property of the StyleSheet.

Most importantly, the rule object also has the styleSheet object. This references the StyleSheet object for the imported stylesheet, and its rules can be referenced and modified in exactly the same way as a normal stylesheet. The imported stylesheet also has the ownerRule property that references the @import rule that imports it.

alert(document.styleSheets[0].cssRules[0].styleSheet.cssRules[0].cssText);

@media blocks

@media blocks allow you to target a selection of style rules to a specific set of media types. The @media rule appears as a single rule in the stylesheet's cssRules collection. The style rules inside it do not appear in the stylesheet's cssRules collection at all. Instead, the rule has its own cssRules collection, that contains all the styles within it. This collection behaves in exactly the same way as the cssRules collection for the StyleSheet object.

The @media rule also has the insertRule and deleteRule methods, which behave in the same way as those for the StyleSheet object. As the @media rule also has a list of media types, it also has the media property, which also functions like that of the StyleSheet object.

All rules inside the @media block also have the parentRule property, which references the @media rule that they are inside. @media blocks may contain other @media blocks, and these can be nested as much as needed. If they are nested, their rules must be accessed by going through the chain of cssRules collections.

alert(document.styleSheets[0].cssRules[0].cssRules[0].cssText);

Test it here: show the contents of the last recognised media block in the first stylesheet.

Getting support for legacy Internet Explorer

Internet Explorer 8- on Windows and Mac does not provide many of the DOM 2 Style Sheets methods or properties, but it has an alternative that is not as complete as the standard, but can handle basic stylesheet manipulation. Internet Explorer 9+ follows the DOM standard only in standards rendering mode. Pages rendered in quirks mode will need to use the Internet Explorer model instead. Pages rendered in IE9+'s IE 7 compatibility mode (enabled automatically or by the user on some pages, or by default for intranet sites) will also not be able to use the standard model. If you need Internet Explorer 8- (or 9+ quirks mode) support, you will have to accept the differences and limitations, make sure your code does not rely on the parts that Internet Explorer is missing, and implement the branching code I will show you here.

In standards rendering mode, Internet Explorer 9+ maintains both the DOM and IE models, which are completely separate. When referencing the same rule using both collections, the returned objects are not the same as each other. The common properties in both models (like selectorText) can hold completely different values, depending on the bugs present in the implementation of that model. This can catch scripts out very easily, so it's best to use the least buggy model - the DOM model.

The styleSheets list

Internet Explorer provides the same collection as the other browsers, and to a large extent it works the same way. The type, disabled, href, and title properties all work the same way. The media property is different. Instead of being a media object, it is a string. As a result, there are no methods to add, remove, or list media types. If you want to change it, you will need to treat it as a string. Trying to set it will throw an error in IE Mac, so it will need a try...catch statement. (Note that in IE 9+ standards mode, IE provides both models - the media attribute follows the standards model.)

var oSheet = document.styleSheets[0];
if( typeof(oSheet.media) == 'string' ) {
  try { oSheet.media = 'screen'; } catch(e) {}
} else {
  oSheet.media.mediaText = 'screen';
}

Test it here: set the media type on all stylesheets on this page to 'print' (the page should appear unstyled when viewed normally), use print preview to check if it worked, then return it to normal.

Each stylesheet can reference the element that creates it using owningElement, in the same way as standards compliant browsers use ownerNode.

var oSheet = document.styleSheets[0];
var oElement = oSheet.ownerNode ? oSheet.ownerNode : oSheet.owningElement;

Although IE on Windows provides the styleSheet property of the LINK or STYLE element, in the same way as standards compliant browsers use sheet, this property is not available in IE on Mac.

var oElement = document.getElementsByTagName('style')[0];
var oSheet = oElement.sheet ? oElement.sheet : oElement.styleSheet;

Test it here: check for these properties.

Stylesheet cssRules collection

Internet Explorer provides the rules collection, in the same way as standards compliant browsers use cssRules. However, its behaviour is sufficiently different and incompatible, so it is not possible to simply index the same rule in each collection. @page, @media, @import, @charset, and @font-face rules are not included in the collection. In IE on Windows, all rules inside a @media block are included in the rules collection of the stylesheet. In IE Mac, they are ignored completely, and are not available to the DOM. In both browsers, it is not possible to modify the @media rule itself. Adding new rules into a @media block is not possible in IE on Windows.

Note that the stylesheet itself has a cssText property in IE, which gives all the CSS in the stylesheet. On Windows, this includes any @media blocks. However, it is not much fun to edit the stylesheet using pattern matching, which is all that this can provide.

IE on Mac provides both the rules and cssRules collections, but treats them both the IE way (so the cssRules collection is too short). Internet Explorer 9+, also provides both collections in standards rendering mode, but this time, they are implemented completely separately - using the IE and DOM models. In IE 9+, you would almost certainly want to use the standard one where it is available, while IE on Mac would want to use the IE model. If you don't care about IE on Mac (it's been discontinued, so you probably don't care about it), then simply check for the DOM collection and use it where available, falling back to the IE model otherwise:

var oSheet = document.styleSheets[0];
var oRule = oSheet.cssRules ? oSheet.cssRules[7] : oSheet.rules[3];

However, it is possible to get support for IE on Mac. Check if the stadard DOM collection is available, and make sure that it is not the same as the IE collection (that check will also work in other browsers that do not provide the DOM collection, since the IE collection will be undefined):

var oSheet = document.styleSheets[0];
var oRule = ( oSheet.cssRules && oSheet.cssRules != oSheet.rules ) ? oSheet.cssRules[7] : oSheet.rules[3];

Adding and removing rules in Internet Explorer (on Windows) is done using the addRule and removeRule methods. The removeRule method is exactly the same as the standard deleteRule method (except of course that the index will be different). The addRule method, however, is very different to the standard insertRule method. Firstly, it requires two different parameters; the selector string, and the rule string. Secondly, it can only add a rule at the end of the stylesheet.

var oSheet = document.styleSheets[0];
if( oSheet.deleteRule ) {
  oSheet.deleteRule(7);
} else if( oSheet.removeRule ) {
  oSheet.removeRule(3);
}
if( oSheet.insertRule ) {
  oSheet.insertRule('body { background: #779; }',oSheet.cssRules.length);
} else if( oSheet.addRule ) {
  oSheet.addRule('body','background: #779;');
}

Test it here: add the new rule 'p { color: lime; }', and then delete it again.

The CSS rule

Since the rules collection only contains normal style rules, the rules do not have a type property. In IE on Windows, they also do not have cssText. They do have a selectorText property though, and (apart from a few bugs) it behaves exactly the same as with standards compliant browsers. Similarly, they also have the parentStyleSheet property.

They also have the all important style property. However, here there are some significant differences. The item method does not exist in IE on Windows. You can try using for(i in styleobject) but that does not return what you specify in the stylesheet. Instead, it steps through all possible styles that IE recognises, whether you actually used them or not. It is actually a fairly short list (since IE does not actually support very many styles), but it makes recognising your own styles very hard.

More importantly, however, IE does not provide most of the other methods of the style object. To read or change styles, you will have to stick to the .style.color syntax. Since this works in all browsers, that is not too difficult to work with. Note that IE will throw errors if you set styles to values that it does not support (such as setting display to 'table'). Note that the .style.cssText property is available, but IE Mac makes a bit of a mess of it.

var oSheet = document.styleSheets[0];
var oRule = oSheet.rules ? oSheet.rules[3] : oSheet.cssRules[7];
oRule.style.color = 'lime';

Test it here: the last rule in the demo stylesheet is currently 'p { }'. Set its color property to 'lime', then set it back to ''.

There is also a StyleSheet.pages collection that gives all @page rules, but it does not give any useful information about the styles in that rule.

@import rules and imported stylesheets

@import rules are listed in their own StyleSheet.imports collection in IE. Each entry in this collection is a StyleSheet object, that is the same as the styleSheet property of the @import rule in standards compliant browsers. It has the same properties as a normal stylesheet.

var oSheet = document.styleSheets[0], oImportedSheet;
if( oSheet.imports ) {
  oImportedSheet = oSheet.imports[0];
} else {
  oImportedSheet = oSheet.cssRules[0].styleSheet;
}

DOM events

This is currently supported by Opera 7+, Mozilla/Firefox, Konqueror 3.0+, Safari/Chrome, Internet Explorer 9+ in standards mode and NetFront 3.3+. It is also partially supported by iCab 3, ICEBrowser 5+, and Tkhtml Hv3. Espial claim that Escape/Evo 5 supports this, but it does not support even the basics.

Note: Internet Explorer 8- on Windows does not comply with the standard here, so if you are testing, use a browser that supports the standard correctly, such as Opera. Internet Explorer 9+ drops support for the standard model in quirks mode and IE 7 compatibility mode, and will continue to need the Internet Explorer 8- workarounds. I will give details later on how to get support for Internet Explorer 8- on Windows as well. Internet Explorer on Mac does not support either the DOM or IE events models.

Problems with the traditional event models

Traditionally, browsers had a simple way to listen for events, and run scripts when that happened. The script could be held inside an appropriate event handler attribute, such as onclick="alert('here');". Internally, the browser would register this as an anonymous function, that a script could override:

referenceToElement.onclick = function () { alert('here'); };

Returning false from any event handler function would prevent the normal action from happening, such as preventing the browser following a link when it detected the click event.

In many basic applications, this was adequate. However, it has limits. If an event handler already exists, and you want to add functionality to it part way through your script, you would have to rewrite the function with both the new and old functionality. Then if you wanted to remove some functionality, you would have to rewrite it again. You might even have to write your own handler function management system, with one event handler that runs the appropriate functions in turn.

Another problem with the traditional model was that it never coped well with the possibility that two elements may detect the same event. Imagine that a link is inside a div, and both the link and the div are set up to detect the click event. When you click the link, should the link see the click? Or should the div see it? Or should both? If both should detect it, in what order should they see it; div first, or link first?

Initially, when Netscape introduced events, their answer was simple; the link should see it. Nothing more. Later, they allowed the div to see it as well, but only if you told it to capture the event. The div would then see the click, and after that, the link would see it (in Netscape's original implementation, the event would only be seen by the link if you explicitly told it to continue after the initial capture - this is not necessary in DOM 2 events). At the same time, Internet Explorer introduced an alternative version. By default, both the link and the div would see the click, and unlike in Netscape, the link would see it first. This was called bubbling.

Adding DOM event listeners

The DOM 2 events module was designed to cope with all possibilities, so not only could you add multiple event handlers for a single event, but you could also say in what order elements should see the event. The DOM refers to these event handler functions as event listeners. The way to add an event listener is like this:

referenceToElement.addEventListener('nameOfEvent',referenceToFunction,phase)

You can add as many listeners for the same event on the same element as you want, but note that there is no way to guarantee in what order they will be run. (Trying to add the same event, function, and phase to the same element multiple times will result in it being added just once.) Note that, unlike with the traditional events models, it is not possible to obtain a reference to the event handler functions (this could previously done with referenceToElement.oneventname, but DOM 2 events does not provide any equivalent to list all the event handlers).

The name of the event is written like this: 'click', 'mouseover', or 'keyup'. The referenced function will be run in much the same way as with the traditional model. According to the spec, the 'referenceToFunction' parameter should actually be a reference to an object that has a method called 'handleEvent', and that method will be called instead. However, in practice, Opera 8+, Mozilla/Firefox, Konqueror 3.2+, Safari/Chrome, Internet Explorer 9+ and iCab implement this correctly, but the others do not. Aditionally, most authors are not even aware that this functionality exists. In most cases, it is easiest to just use a direct function reference.

The phase says in what order the event should be detected. False means the bubbling phase, and is usually the one you want to use. True means the capturing phase. Any event handlers added using the traditional techniques are treated as a bubbling event listener. The phase is actually very important to get right, since its behaviour is not straightforward. Capturing is special. According to the specification, if an element is set up to capture a click event (for example), and you click it, the event handler should not be run. It should only be run if you click on a child element inside the element. In other words, the capture phase is only used on ancestors of the target element.

In our earlier link and div example, assume that both elements are set up to detect the click event using both the capturing and bubbling phase. If you click the link, the specification says that the event listeners should be run in this order:

  1. Capture listeners for the div
  2. Bubbling listeners for the link
  3. Bubbling listeners for the div

This progression from listener to listener is known as propagation. You can check what stage the propagation is at by checking the eventObject.eventPhase property. It should be 1 during the capture phase, 2 during the bubble phase on the target element itself (in this case the link), and 3 during the bubbling phase on the target's ancestors. It can also be 0 if it is a manually created event object that has not yet been fired. See below for further details.

Note that some events (such as the load event) do not bubble up to ancestors, but they will still capture, so capture phase listeners will always see the events of their children (with load events detected in the capture phase on the document, this means they should see an event every time any image, plugin, or many other embedded file types load).

Also note that in the above example, only Opera 9.2-, Safari 1.1-, NetFront 3 and ICEBrowser follow the specification. Mozilla/Firefox, Opera 9.5+, Konqueror, Chrome/Safari 1.2+, Internet Explorer 9+, Netfront 4+ and iCab will fire the capture listeners on the link. Mozilla 1.7-/Firefox 1.0 will fire it before point 3. Opera 9.5+, Konqueror, Safari, iCab and Mozilla 1.8+/Firefox 1.5+ before point 2 giving this (commonly supported) event propagation:

  1. Capture listeners for the div
  2. Capture listeners for the link
  3. Bubbling listeners for the link
  4. Bubbling listeners for the div

That behaviour is wrong according to DOM 2 Events, but it is relied on by many Web pages, so it it unlikely that the browsers will follow the DOM 2 Events specification. It may be changed to be correct in DOM 3 (though it currently is not), in which case the other browsers can be expected to implement it that way as well. However, you should be aware that browsers may behave either way. Do not rely on the capturing phase firing on the target, since it is currently incompatible, and may well be changed at some point.

So now, to put this example into practice:

<div id="thediv"><a href="/" id="thelink">test</a></div>
...
var oDiv = document.getElementById('thediv');
var oLink = document.getElementById('thelink');
oDiv.addEventListener('click',function (e) {
  alert('1. Div capture ran');
},true);
oDiv.addEventListener('click',function (e) {
  alert('3. Div bubble ran');
},false);
oLink.addEventListener('click',function (e) {
  alert('Link capture ran - browser does not follow the specification');
},true);
oLink.addEventListener('click',function (e) {
  alert('2. Link bubble ran (first listener)');
},false);
oLink.addEventListener('click',function (e) {
  alert('2. Link bubble ran (second listener)');
},false);

Test it here: click this link to activate the event listeners.

Note that some browsers will run the first link bubble listener before the second, and some will run the second before the first - the standard does not say in which order they should be run, and it even allows them to be run at the same time - you should not rely on any specific behaviour here).

Removing event listeners

Removing event listeners is as easy as adding them:

referenceToElement.removeEventListener('nameOfEvent',referenceToFunction,phase)

Note that you can remove event listeners at any time, and you must remove them using the exact same parameters as you added them with. This can be difficult if you used anonymous functions as I did in the example above. If you use anonymous functions, you can use the arguments.callee property provided by JavaScript to reference the function and remove the listener. This reference can only be done from inside the listener itself. So for example, to add an event listener that removes itself after it has fired once, you can use:

oLink.addEventListener('click',function (e) {
  alert('Link bubble fired');
  this.removeEventListener('click',arguments.callee,false);
},false);

Test it here: fire the event listener and see if it can remove itself (you should get an alert the first time you click the link).

Referencing the elements that detect the event

You may notice that I use the 'this' keyword in the last example. Inside an event listener function, the 'this' keyword is a reference to the element that is currently detecting the event. In the first example, that could be the div, or the link, depending on which one the event listener is attached to. You may also notice that the functions accept a parameter (I called it 'e', but you could call it whatever you want). That parameter, as with the original Netscape event model, is the event object, and contains a lot of information about the event. For now, I will stick with the basics.

eventObject.currentTarget is the same as 'this' - it refers to the element that the listener function is attached to (if the same function is attached as a listener to several elements, it will be a reference to the listener that is currently being run).

eventObject.target is the element that the event actually happened to. In the case of the earlier example, this would be the link. If there was a span inside the link, and you clicked that, then eventObject.target would be a reference to the span, even if the span itself did not have any listeners attached directly to it. Note that older Safari and Konqueror sometimes make the target point to the textNode inside the element. That is incorrect behaviour, and is fixed in current versions. If this is likely to cause problems, check the nodeType of the target. If it is a text or CDATA node, then you will need to use its parent node instead.

var theTarget = e.target;
if( theTarget && ( e.target.nodeType == 3 || e.target.nodeType == 4 ) ) {
  theTarget = theTarget.parentNode;
}

Test it here: click anywhere within this sentence to see what element you clicked.

Some events (such as mouseover) will also provide eventObject.relatedTarget. This will be a reference to the element the mouse was over before it was moved over the current element. With mutation events, it is typically (but not always) the parent node of the node being manipulated.

Obtaining other information from the event

Information such as mouse button and position, keyCode, ctrlKey, altKey, shiftKey, and detail (such as click count) are available in the same way as with the older model (see the HTML events section of this tutorial for more details). They are available as properties of the event object. Mutation events can also provide prevValue, newValue, attrName and attrChange values (see the DOM 2 events specification for more details).

Preventing the default action

In the example above, clicking the links will activate the listeners, but the link will be followed anyway. Returning false from a listener function does not work like it used to in the traditional models. Instead, you must use eventObject.preventDefault() - only one event listener needs to run this method for the default action to be prevented.

Not all event types can be cancelled (the load event, for example, has no default action, and cannot be cancelled). If an event is of a type that can be cancelled, the eventObject.cancelable property will be true.

oLink.addEventListener('click',function (e) {
  alert('Link detected a click. Cancellable: '+e.cancellable);
  e.preventDefault();
},false);

Test it here: if you click this link, you should remain on this page.

Preventing other listeners from seeing the event

When an event is being processed by the event listeners, it is possible for any event listener to prevent any more event listeners in the propagation chain from seeing the event. In the example above, the div's capture phase click event listener could prevent the other event listeners from seeing the click event, by using the eventObject.stopPropagation() method. Note that you can check if the event is a type that can bubble by checking the eventObject.bubbles property.

The stopPropagation method only prevents listeners further along the propagation chain from seeing the event. For example, I added two event listeners to the link in the bubble phase. If one of these uses the stopPropagation method, the other one will always run, no matter in what order the browser ran them. The method would only stop the event listener that was added to the div in the bubble phase.

<div id="thediv"><a href="/" id="thelink">test</a></div>
...
var oDiv = document.getElementById('thediv');
var oLink = document.getElementById('thelink');
oDiv.addEventListener('click',function (e) {
  alert('Div capture ran. Now stopping propagation.');
  e.stopPropagation();
},true);
oLink.addEventListener('click',function (e) {
  alert('This alert should never appear');
},false);

Test it here: click this link to activate the event listeners.

More than basic events

As well as the basic events defined in HTML, DOM 2 provides aditional event types that can be detected. A useful example of these are the mutation events. These fire when elements or descendant nodes are added, modified, or removed by a script (I will not go into more detail here, see the DOM 2 events specification for more details). They are partially supported by Opera, Mozilla/Firefox, Konqueror, Safari/Chrome and Internet Explorer 9+. Note that browser support for 'DOMNodeInsertedIntoDocument', 'DOMNodeRemovedFromDocument', and 'DOMSubtreeModified' events is currently very poor, so these events should not be used. Safari 4- also does not work with the 'DOMAttrModified' event. Note that Opera 7.1, Safari 1.2, and Konqueror 3.2-3.3 will crash if you attempt to use any mutation events. Opera 7.2+, Safari 1.3+, and Konqueror 3.4+ will work correctly. Konqueror 4.2.2 seems to have lost all support for all mutation events.

The DOM does not provide any way to work out if the event will fire or not. If a browser does not support an event, it will still allow you to add the event listener, but it will not fire the event. It is possible to check if a browser supports the required events model using hasFeature, but this is not a perfect indication, since a browser will only claim to support the feature if its support is relatively complete. Browsers may support a large amount of the feature (enough for what you need) but still not claim to support it. Assuming you want a relatively complete implementation, instead of support for just one or two event types, checking for mutation events is as simple as this:

if( document.implementation.hasFeature('MutationEvents','2.0') )

Note that this will exclude Mozilla/Firefox, even though it does support many of the common mutation events. You could alternatively check for the interface as well, since Mozilla/Firefox (Opera, newer Safari/Chrome and Internet Explorer 9+ provide it too, but not Konqueror or older Safari) should provide this:

if( document.implementation.hasFeature('MutationEvents','2.0') || window.MutationEvent )

Some other types of event may also cause problems, not because the browsers do not support them, but because the browsers support them only on the wrong object. The load event is the worst behaved in this respect. According to the original DOM 2 events specification, the load event should be detected on the document object. Unfortunately, the traditional models used the body tag, and mapped it to the window object. As a result, when browsers added support for it via DOM, most of them initially chose to detect it only on the window object. Future specifications will make it more clear that it should only work with the window object.

Opera 10.1-, and similarly aged versions of Konqueror, Safari/Chrome and NetFront correctly detected it on the document object (as well as on the window object, for compatibility reasons). This was changed in Opera 10.5+, and similarly aged versions of the other browsers, to follow the Mozilla behaviour. Internet Explorer 9+ also follows the Mozilla behaviour. New implementations may theoretically follow the DOM specification, and only detect it on the document object. For this reason, you should check if the window and document objects can detect events before adding listeners to them. If the browser allows you to attach event listeners to the window, you can assume that it can detect the load event there. This is true for all current implementations.

Note also that as described above, you should always use the bubble phase when detecting load events, or they will repeatedly fire when other embedded content loads. However, note that Mozilla/Firefox and iCab 3 cannot capture load events, Opera 9.5+, Safari/Chrome, Internet Explorer 9+ and Konqueror only capture load events when the listener is attached to the document (not the window), and only Opera 9.2- and ICEBrowser correctly always capture load events. Unless you really know what you are doing, and you have worked around all the incompatibilities, you should only ever detect load events in the bubbling phase. Most importantly, do not rely on the implementations of browsers that do not capture load events; they are wrong, and the same code will not work the same way in other browsers.

if( window.addEventListener ) {
  window.addEventListener('load',handlerFunction,false);
} else if( document.addEventListener ) {
  document.addEventListener('load',handlerFunction,false);
}

Bad news; your browser did not detect the load event using this script.

The situation is similar for several other events, such as scroll and resize. They will also require you to use the window and document branches.

Manually firing events

With the old events model, it was easy to fire event handlers manually, using referenceToElement.oneventname() - since there was only one possible handler, and it was treated as a method, it could be run as easily as any other method. With the DOM 2 events model, this is not possible, since there may be multiple handlers for the same event. The DOM provides a few methods that can be used to create and prepare a fake event object, then use it to fire the event on any target.

The element you fire an event on does not have to be listening for that event, since potentially, the parent element may also be listening for that event.

Note that manually firing an event does not generate the default action associated with that event. For example, manually firing a focus event does not cause the element to receive focus (you must use its focus method for that), manually firing a submit event does not submit a form (use the submit method), manually firing a key event does not cause that letter to appear in a focused text input, and manually firing a click event on a link does not cause the link to be activated, etc. In the case of UI events, this is important for security reasons, as it prevents scripts from simulating user actions that interact with the browser itself.

There are five DOM 2 event modules, and they map directly to the names of the DOM features;

Events
Covers all event types.
HTMLEvents
Covers 'abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', and 'unload'.
UIEvents
Covers 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', and (since they do not have their own key events module in DOM 2) it also covers 'keydown', 'keypress', and 'keyup'. Also indirectly covers MouseEvents.
MouseEvents
Covers 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', and 'mouseup'.
MutationEvents
Covers 'DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMCharacterDataModified', 'DOMNodeInsertedIntoDocument', 'DOMNodeRemovedFromDocument', and 'DOMSubtreeModified'.

Other events (such as 'dblclick') are not part of the specification, and browsers are not required to support them. Browsers that do support them will either choose an appropriate event module to class them as - dblclick would fit very well into MouseEvents - or they will create a new module. You will need to know what module the browser has put the events into when you create these events.

Fake event objects are created using the document.createEvent method, and the appropriate event module should be specified when creating it. They are then prepared using the appropriate method, and finally they are then fired on the desired target element using the element.dispatchEvent method. If you use the more specific event type instead of the generic event type, you can provide more information when preparing it. dispatchEvent will return false if the detault action was prevented.

The event object methods for preparing the event object properties for the various event modules are:

HTMLEvents and generic Events
initEvent( 'type', bubbles, cancelable )
UIEvents
initUIEvent( 'type', bubbles, cancelable, windowObject, detail )
MouseEvents
initMouseEvent( 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget )
MutationEvents
initMutationEvent( 'type', bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange )
Note, although prevValue, newValue, and attrName can all be null, attrChange must always be provided, even if the event is not a DOMAttrModified event (set it to 1, 2, or 3)

MouseEvents are a subclass of UIEvents, and all event modules are a subclass of Events, so if you create a mouse event object, it will also inherit the initUIEvent and initEvent methods from the more generic UIEvents and Events interfaces. You can choose which init*Event method you want to use when preparing the event.

The following is an example of firing a click event:

var fireOnThis = document.getElementById('someID');
var evObj = document.createEvent('MouseEvents');
evObj.initMouseEvent( 'click', true, true, window, 1, 12, 345, 7, 220, false, false, true, false, 0, null );
fireOnThis.dispatchEvent(evObj);

Test it here: move your mouse over here and I will manually fire a mouseup event instead.

The following is another example of firing a click event, this time using the generic initEvent method, and leaving undefined properties at their default values (even when using the generic initEvent or initUIEvent, you should still use the exact module name, and not a generic module, or it will not work in Mozilla/Firefox, Konqueror, or Safari/Chrome):

var fireOnThis = document.getElementById('someID');
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( 'click', true, true );
fireOnThis.dispatchEvent(evObj);

Key events are a little harder, because Mozilla/Firefox does not implement the standard correctly, and does not allow you to create key events using generic UIEvents (it may just crash). Instead, it provides a proprietary module 'KeyEvents', and initialisation method 'initKeyEvent'. The proprietary initKeyEvent method expects the following parameters: 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode. You can use if(window.KeyEvent) to detect support for the KeyEvents module. Browsers that expect you to use UIEvents do not have a specific initialisation function for key events, so some properties must be added manually:

var fireOnThis = document.getElementById('someID');
if( window.KeyEvent ) {
  var evObj = document.createEvent('KeyEvents');
  evObj.initKeyEvent( 'keyup', true, true, window, false, false, false, false, 13, 0 );
} else {
  var evObj = document.createEvent('UIEvents');
  evObj.initUIEvent( 'keyup', true, true, window, 1 );
  evObj.keyCode = 13;
}
fireOnThis.dispatchEvent(evObj);

Getting support for legacy Internet Explorer on Windows

Internet Explorer 8- on Windows does not provide any of the DOM 2 Events methods, and provides very few of the properties of the event object. Internet Explorer 9+ also does not support the DOM 2 Events methods on pages that trigger quirks mode. Pages rendered in IE9+'s IE 7 compatibility mode (enabled automatically or by the user on some pages, or by default for intranet sites) will also not be able to use the standard model. Version 5.0+ (including IE9+ in quirks or standards mode) does provide a similar system that is much more limited in its capabilities, but if you need legacy Internet Explorer support, you will have to accept the differences and limitations, make sure your code does not rely on the parts that Internet Explorer is missing, and implement the branching code I will show you here.

Adding and removing listeners

Adding and removing event listeners is done using these methods:

referenceToElement.attachEvent('onNameOfEvent',referenceToFunction);
referenceToElement.detachEvent('onNameOfEvent',referenceToFunction);

These are almost synonymous with the addEventListener and removeEventListener methods, but they are unable to specify a phase. The Internet Explorer model only detects events in the bubbling phase, not capture. It also requires 'on' to be included at the start of the eventname, so for the click event, you should use 'onclick' in IE, and not 'click'. (Note also that IE 8- only supports the basic event types - it does not support any of the advanced types, such as mutation events.) If you need legacy Internet Explorer support as well, you should use this code:

function eventHandler(e) {
  alert('Div bubble ran');
}
var oDiv = document.getElementById('thediv');
if( oDiv.addEventListener ) {
  oDiv.addEventListener('click',eventHandler,false);
} else if( oDiv.attachEvent ) {
  oDiv.attachEvent('onclick',eventHandler);
}

Test it here: this text has been set up to detect click events, and should alert a message when clicked.

Referencing the elements that detect the event

Internet Explorer's model is more annoying here. The 'this' keyword is a reference to the window object. There is no equivalent to the eventObject.currentTarget property. Internet Explorer's model makes it impossible to see what element is currently processing the event.

It is possible to reference the actual target of the event, using the eventObject.srcElement property, which is equivalent to the eventObject.target property provided by DOM 2 Events. Unlike the way it works with traditional event models, IE does correctly provide the event object as an argument to the handler function when you use attachEvent. There is no need to check if window.event is needed instead, because it is not needed. However, it is important to note that in IE, the event object only exists for as long as the initial event propagation thread. It cannot be stored and reference later, or used in timeout threads. To use srcElement, simply detect what you need (make sure the standards compliant check is made first):

var theTarget = e.target ? e.target : e.srcElement;
if( theTarget && ( theTarget.nodeType == 3 || theTarget.nodeType == 4 ) ) {
  theTarget = theTarget.parentNode;
}

Test it here: click anywhere within this sentence to see what element you clicked.

You can also do the same to get the equivalent to eventObject.relatedTarget - which is known as eventObject.fromElement in IE. It is available for mouseover and mouseout events.

var relTarget = e.relatedTarget ? e.relatedTarget : e.fromElement;

Preventing the default action

The IE model does not provide eventObject.preventDefault() but provides an alternative. The event object has a property called 'returnValue' that will perform the equivalent functionality if it is set to false. Setting this value in other browsers will not cause any problems, so the easiest way to support IE as well is like this:

if( e.preventDefault ) { e.preventDefault(); }
e.returnValue = false;

Test it here: if you click this link, you should remain on this page.

It is not possible to check if the event is a type that can be cancelled in IE.

Preventing other listeners from seeing the event

The IE model does not provide the stopPropagation method (probably because it only supports bubbling). Instead, it uses a property called 'cancelBubble', which must be set to true to prevent propagation. It is not possible to check if the event is a type that can bubble in the IE model.

if( e.stopPropagation ) { e.stopPropagation(); }
e.cancelBubble = true;

Test it here: click this link to activate the event listeners.

Detecting the load event

Legacy Internet Explorer detects the load event on the window object, using the usual attachEvent method. This makes the load event listener (shown above) require one more branch:

if( window.addEventListener ) {
  window.addEventListener('load',handlerFunction,false);
} else if( document.addEventListener ) {
  document.addEventListener('load',handlerFunction,false);
} else if( window.attachEvent ) {
  window.attachEvent('onload',handlerFunction);
}

Bad news; your browser did not detect the load event using this script.

Manually firing events

This is a little more messy than the other event handling, since the initialisation is considerably different. The Internet Explorer model has a method for manually firing events, called fireEvent, and it is available in IE 5.5+ (there is no equivalent in IE 5.0). In its simplest form, this is similar to the DOM version, but the bubbles and cancelable properties are not available. The fireEvent method expects to be passed either one or two parameters. The first should be the name of the event (for example; 'onchange'), and the optional second parameter should be an event object to be passed to the handler.

Note that even though the window object can detect several events in IE (such as load, unload, scroll, and resize - all of which should, according to the spec, be detected by the document object, not window), it is not possible to use fireEvent on the window object.

var fireOnThis = document.getElementById('someID');
if( document.createEvent ) {
  var evObj = document.createEvent('MouseEvents');
  evObj.initEvent( 'mousemove', true, false );
  fireOnThis.dispatchEvent(evObj);
} else if( document.createEventObject ) {
  fireOnThis.fireEvent('onmousemove');
}

If you want to specify more details, you will need to use the createEventObject method in IE to replicate the createEvent and init*Event methods of the DOM. The createEventObject method normally returns a blank event object, and you will need to define parameters yourself. You can optionally pass an existing event object to createEventObject, and it will use that as a template when preparing the new event object. Usually, you will not need to pass it any arguments.

var fireOnThis = document.getElementById('someID');
if( document.createEvent ) {
  var evObj = document.createEvent('MouseEvents');
  evObj.initMouseEvent( 'mousemove', true, false, window, 0, 12, 345, 7, 220, false, false, true, false, 0, null );
  fireOnThis.dispatchEvent(evObj);
} else if( document.createEventObject ) {
  var evObj = document.createEventObject();
  evObj.detail = 0;
  evObj.screenX = 12;
  evObj.screenY = 345;
  evObj.clientX = 7;
  evObj.clientY = 220;
  evObj.ctrlKey = false;
  evObj.altKey = false;
  evObj.shiftKey = true;
  evObj.metaKey = false;
  evObj.button = 0;
  evObj.relatedTarget = null;
  fireOnThis.fireEvent('onmousemove',evObj);
}

Test it here: move your mouse over here and I will manually fire a mouseup event instead.

DOM objects and methods

This gives all properties, collections and methods of the W3C DOM that can be reliably used in all major DOM browsers, as well as document.styleSheets, which is much less reliable, but useful if it is available.

Contents

Key

Each can be clicked for more information.

Collections will also have the length property, giving the number of entries in that collection.

The document node

Creating nodes

To walk the DOM tree

DOM support

All nodes

To walk the DOM tree

Attaching, copying or removing nodes

Node information

Element node attributes

Element node style

Element node size and position (not standardised)

Table and associated nodes

In addition to the normal node functionality, nodes belonging to tables have extra functionality, specially designed to work with the layouts of tables. All elements have their standard HTML attributes available as properties (using the same casing pattern as style properties).

To walk the DOM tree

Attaching, copying or removing nodes

Node information

Stylesheets (if supported)

Events

Adding and removing event listeners

Note that although the methods are attached to all nodes, browsers will only be able to detect events on element nodes, the document node, and the window object.

Creating event objects

Preparing event objects

Firing events

Additional event object methods and properties

As well as the traditional event object properties, some more are provided by DOM events (or IE events) capable browsers.

Last modified: 11 May 2011

This site was created by Mark "Tarquin" Wilton-Jones.
Don't click this link unless you want to be banned from our site.