You are on page 1of 265

JavaScript tutorial - 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:

• They are at work and they can only use the system default browser.
• They want to view pages more quickly so they use a text only browser.
• They are blind or have poor eyesight so they need a text only browser/reader/Braille reader.
• They realise that the more extra features like JavaScript or VBScript you use, the more you
leave yourself open to becoming a target for viruses or vulnerability exploits.
• They don't like the way some sites insist on taking over their control of the browser, opening
popup windows, breaking the operation of the back button, preventing the context menu from
working, or insisting on making things appear and disappear in a way that causes usability
problems.

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 exception is a cookie, which can be used by many
pages to 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, 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 theterms 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

• Preloading images for use in mouseovers and pre-caching.

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

• Opening a window with customisable settings.

The next most popular use of JavaScript; writing dynamic content

• Changing what is written depending on conditions.

The next most popular use of JavaScript; checking forms

• Form checking, ensuring a form is filled in correctly.

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

• Complicated string manipulation, taking variables from the location bar.


• Creating, reading and modifying cookies.

Some specialist scripts

• Form manipulation, offering the correct countries for a continent.


• More form manipulation, expandable tree structure.

The most irresponsible script

• Browser sniffing - to be used only to help avoid browser specific bugs, not to determine
capability.
DHTML

Appearing / disappearing content

• Popup Menus.
• Popup Menus with delay - see Gecko engine bug.
• Auto-generation Popup Menus with delay - Gecko engine bugs removed.
• Tooltips.

Movable content

• Tooltips.
• Static logo.
• Falling objects (snow).
• Flying stars (changes apparent size of div elements).
• Fireworks (demonstrates changing colour).
• Analogue clock.
Mouse trails

o Single trail with bouncing animation.


o Multiple trail with several possibilities.

Rewritable document content

• Generic re-writable elements.


• (Simple) message scroller.

W3C DOM

• Importing XML data files - a very advanced scripting topic.


• Solitaire game - a very advanced script.

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 VERY demanding on resources. The simpler games may even work in 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:

• Most scripts can go inside the document head. This keeps them out of the way of the main
document content.
• If your script needs to run at a certain stage during page layout (for example, if it uses
document.write to create content), it should be put at the correct stage of the document, almost
always somewhere inside the document body. If the script is very small, then put it all where it
will be used. If it is larger, then put it inside the head inside a function, and you can then call that
function when you need it.
• If your script is used on more than one page, or if it is of any significant size, then put it in its
own file, and load it in the document head. Not only will this help to keep the clutter of the
document, but it will also help avoid potential syntax problems (I will cover these later). As an
extra benefit, these can be used by multiple pages, allowing browsers to use their cache, and
saving bandwidth for you and your visitors.

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
ofapplication/xhtml+xml - the majority of pages that use XHTML markup are actually served
as text/htmland 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

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
writesubmit('name of form')

That would simply be a function. In object oriented programming like JavaScript, we would
usedocument.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:

• With the non-object-oriented way, we would tell a function to submit the form.
• With the object oriented way, we would tell the form to submit itself.

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. There are many JavaScript 1.3
operators such as the identity operator === and others like it. These are supported by all current
browsers, but are rarely used. Browsers that do not understand them will just produce errors.

JavaScript operators

Opera
Uses
tor

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
JavaScript operators

Opera
Uses
tor

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)


JavaScript operators

Opera
Uses
tor

in object or array on the right must have the property or cell on the left

Note, if you do not set the language="javascript1.2" attribute in the <script> tag, 0 == false ==
'' andundefined == 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.

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

Returns the angle (rads) from cartesian


Math.atan2(n,k)
coordinates 0,0 to n,k

Returns n rounded up to the nearest whole


Math.ceil(n)
number

Math.cos(n) Returns cos n (where n is in radians)

Math.exp(n) Returns en

Returns n rounded down to the nearest whole


Math.floor(n)
number

Returns ln(n)
Math.log(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 object methods

Operator What it does

Math.pow(n,k) Returns nk

Math.random() Returns a random number between 0 and 1

Returns n rounded up or down to the nearest


Math.round(n)
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)

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 and 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. 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 undefineduntil a statement is executed that gives it a value.

Objects
• WebTV 2.6- does not support the {} syntax for creating 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
• WebTV 2.6- does not support the [] syntax for creating 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 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.

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


saydocument['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. 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. This will cause an error:

var a = b + 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;

In the next example, the parent object has been defined, but it is a type of variable that cannot
accept child properties. 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'


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 or
option,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, usingmyNewVariable = '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 ofmyNewVariable 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 Value Mathematical


property (approx) equivalent

Math.E 2.718 e

Math.LN2 0.693 ln(2)

Math.LN10 2.303 ln(10)

Math.LOG2E 1.442 log2(e)

Math.LOG10E 0.434 log10(e)

Math.PI 3.142 Pi

(sqrt(2))-1 or sqrt(1/2
Math.SQRT1_2 0.707
)

Math.SQRT2 1.414 sqrt(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 popular among Mac users - probably a little less
than 50% of the Mac market). 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:

• Netscape 4 (inventors of JavaScript), Escape, Opera 7.6- and ICEbrowser include the
linebreaks in the string.
• Internet Explorer, Mozilla/FireFox, Opera 8+, Safari/Konqueror, iCab and OmniWeb ignore
the linebreak characters.
• ICEbrowser sometimes ignores the whole string

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

• Opera 4-6 has a major bug in its regular expression handling and will fail on even the simplest regular
expressions.
• Pocket Internet Explorer, Internet Explorer for Windows CE and iCab 2- do not support regular expressions
properly.

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

• Netscape 4, Internet Explorer 4 and WebTV do not support this structure and will produce errors if you use
it.

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 is its major failing. Thankfully these browsers are hardly used
any more. It would be very 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.taintEnabledcauses errors in 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.

It should never be used to detect if a browser supports a method or property


likedocument.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'

• WebTV crashes if you use the 'in' operator to check for a property that does not exist.
• Netscape 4, Internet Explorer 5.0- on Windows and Internet Explorer on Mac cannot use the 'in' operator as
shown here.

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 thetypeof 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">

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 really 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

• Pre-alpha versions of Tkhtml Hv3 do not correctly interpret the Function class constructor.

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 usearguments.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:

• nameOfFunction(listOfVariables);
• window.nameOfFunction(listOfVariables);
• object.onEventName = nameOfFunction;

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

Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade!

Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade! Fade!

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 thesample 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.ato 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 given below.
The problem

This problem is encountered with event handlers and in other situations, such as when adding
methods to an object. Most Web authors first encounter it with event handlers, so that is what this
article will concentrate on, but you should be aware that it also applies to those other situations.

In JavaScript, whether using traditional event handlers, or DOM events, event handlers are attached
to their relevant object as a reference to the function. In standards compliant browsers, when the
event fires, the function is executed as a method of the object, and passed a single parameter; the
event object. In Internet Explorer, it is not passed any parameters at all:

function myhandler(e) {

...

mydiv.onclick = myhandler;

mydiv2.addEventListener( 'click', myhandler, false );

There are many occasions when it is desirable to pass a local variable to the handler function. An
example would be when looping through a list of elements, and assigning the same event handler to
each of them, but needing to pass a unique identifier to each handler so it can always reference the
index of the element within a collection. You may also want to pass an object, not just an index.
Perhaps this would be a reference to another element that is relevant to the handler.

The obvious desire is to try something like this:

mydiv.onclick = myhandler(i,myobject);

However, this does not work. What it will do is immediately execute the handler as a normal function,
passing it the variables. Then whatever the function returns, even if that is a string, or perhaps
nothing at all, is assigned as a property of the element with the name of the event handler. Obviously
this will not work correctly as an event handler.

There are three main ways to overcome the problem. The first two are the most common, but have
limitations. The third is one of the most complete solutions, but is not very common. The
demonstrations are all prepared assuming the following example algorithm:

function myfunction() {

var paras = document.getElementsByTagName('p');

var spans = document.getElementsByTagName('span');

for( var i = 0; i < paras.length; i++ ) {

paras[i].onclick = function (e) {

//assume that here, we want to know

// - what the value of i was for this element

// - the span that corresponds to spans[i]

};

...

myfunction();

In that example, the event handler is executed in the scope of the outer function, so paras, spans,
and i are all preserved. However, they will have the values that they have in that scope at the time
the handler is executed, not the values they had at the time the assignment was made. This means
that (except in extremely rare cases where the event handler is executed before the end of the loop
is reached), i will hold the value of paras.length, and not the value we need.

The solutions are:

1. Using the Function constructor


2. Assigning variables as properties of the element
3. Using scope to remember the instances of the local variables
Using the Function constructor

This is the most limited and problematic approach, but it is the most commonly used - typically
because the author was not aware of the alternatives or their advantages. The idea is to write the
function code as a string, with the local variables added to the string. When the event handler runs, it
is evaluated in the current scope (not the scope it was created in). The problems with this approach
are numerous:

o It has very poor performance. This comes from the conversion from string to
executed code (similar toeval), and the creation of multiple functions that cannot be optimised
by the JavaScript engine into a single stored function.
o The local scope variables are lost. In this case, that means that the
variable spans no longer exists.
o It can only pass literals (strings, numbers, booleans, regular expression patterns,
null), and not objects or arrays directly into the code. They can be referenced by name but only if
they are available in the global scope, or the scope that the event handler will be executed in.

This means that although it is possible to pass i into the function code, spans will need to be made
global, and the elements in it referred to using the index i as part of the function code:

var spans;

function myfunction() {

var paras = document.getElementsByTagName('p');

spans = document.getElementsByTagName('span');

for( var i = 0; i < paras.length; i++ ) {

paras[i].onclick = new Function(

'var a = '+i+', b;\n'+

'b = spans[a];\n'+

'... etc.'

);

}
...

myfunction();

Note that the objects returned by getElementsByTagName are dynamic, so if the number of spans
changes between creating the event handler and running it, the spans object will be updated, and
the index will not work.

Assigning variables as properties of the element

The idea for this approach is to store all the variables that will be needed as properties of the
element that detects the event. Then when the function is executed, it can look for these properties
on the element, referenced using the 'this' keyword.

This approach works well in most situations, but can have problems. Say for example that you want
to use the same function multiple times, attaching it as a handler on the same element for multiple
events, where each one expects different variables to be available. It is possible to work around this,
but it can get messy. If using this, it is important to make certain that the chosen variable names will
never be used by any browser as part of the DOM properties for that element. This also adds weight
to the element objects, and is not the cleanest approach, but it does work:

function myfunction() {

var paras = document.getElementsByTagName('p');

var spans = document.getElementsByTagName('span');

for( var i = 0; i < paras.length; i++ ) {

paras[i].originalindex = i;

paras[i].relatedspan = spans[i];

paras[i].onclick = function (e) {

var a = this.originalindex, b = this.relatedspan;

... etc.

};

}
}

...

myfunction();

Since this executes the event handler in the local scope, it would also be possible to reference the
span using:

b = spans[this.originalindex];

Note that again, the objects returned by getElementsByTagName are dynamic, so if the number of
spans changes between creating the event handler and running it, the spans object will be updated,
and the index will not work.

Using scope to remember the instances of the local variables

This is the most complete and advanced approach, and solves all of the problems created by the
other techniques. The idea is to draw on the initial mistake of executing a function, and use that to
our advantage. Make it run a function, passing it the parameters. That function returns another
function that exists inside it, within its local scope. This becomes the event handler when it is
assigned, and is executed inside the local scope of the outer function, with the local variables that
were passed to it preserved within that scope. This is known as a 'closure':

function scopepreserver(a,b) {

return function () {

//do something with a and b

... etc.

};

function myfunction() {

var paras = document.getElementsByTagName('p');

var spans = document.getElementsByTagName('span');


for( var i = 0; i < paras.length; i++ ) {

paras[i].onclick = scopepreserver(i,spans[i]);

...

myfunction();

The main problem with this approach is its lack of understanding, due to the number of authors who
are not aware of it, and may be confused by what the code is attempting to do. You can always
leave a comment in your code pointing them to this article. If you have trouble understanding it
yourself, I suggest you see thefunction scope part of my JavaScript tutorial, where I describe how
scopes like this work.

There is also the point that the handler function and the scope relating to it are preserved indefinitely
and can use up memory. However, this is no worse than the other alternatives, which can also hold
on to large numbers of variables indefinitely. You can always remove the handlers once you are
finished with them, which will allow the garbage collector to free up the memory they used.

If you do not want to use an external function, it is also possible to define the scope preserving
function inline, and execute it immediately. This has the added advantage that it preserves the scope
of the main function (myfunction in the examples) as well as the inner scopes, but has the
disadvantage that it becomes less easy for others to understand:

function myfunction() {

var paras = document.getElementsByTagName('p');

var spans = document.getElementsByTagName('span');

for( var i = 0; i < paras.length; i++ ) {

paras[i].onclick = (function (a,b) {

return function () {

//do something with a and b

... etc.
};

})(i,spans[i]);

...

myfunction();

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

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.

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:
• body, referred to as document
• div, referred to using browser specific references, targetting its id
• span, referred to using browser specific references, targetting its id (it is best not to reference
this for 4thgeneration DHTML as layers browsers have bugs that will show up if you do)
• img, referred to through the document object targetting its name
• a, referred to through the document object targetting its index
• form, referred to through the document object targetting its name
• input, select or textarea, referred to through the form targetting its name

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, Gecko browsers (Mozilla/Firefox/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 full list of objects, properties,
collections and methods, refer to the section on The JavaScript object.

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.
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 4 th generation
browsers, and move on to the much more advanced DOM browsers (also known as 5th generation
browsers).

Contents

• Standard document components - all addressable components of a web page.


• Positioned elements - all addressable components of an absolutely positioned div.
• Event objects - all accessible information about an event.
• Intrinsic objects - variable types with constructors.
Key

• Parent object
o Child object
o Child property
o Child object being accessed through a collection[]
o Event
o Method()

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, calledvarName 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

• window or self
o Array
o Boolean
o closed
o Date
 parse(string dateAsAString)
o defaultStatus
o document
 bgColor
 body
 clientHeight
 clientWidth
 scrollLeft
 scrollTop
 style
 background
 backgroundColor
 color
 cookie
 documentElement
 clientHeight
 clientWidth
 scrollLeft
 scrollTop
 style
 background
 backgroundColor
 color
 domain
 fgColor
 IdOfPositionedElement
 lastModified
 nameOfForm
 action
 encoding
 length
 method
 name
 nameOfInput/textarea/select or nameOfInputsSharingName[int
index]
 checked
 defaultChecked
 defaultValue
 disabled
 form
 length
 name
 selectedIndex
 type
 value
 options[int numberOfOptionWithSelectInput]
 defaultSelected
 index
 selected
 text
 value
 onblur
 onchange
 onclick
 ondblclick
 onfocus
 onkeydown
 onkeypress
 onkeyup
 onmousedown
 onmouseup
 blur()
 focus()
 select()
 target
 elements[int numberOfInput]
 onreset
 onsubmit
 reset()
 submit()
 nameOfImage
 border
 complete
 height
 lowsrc
 name
 src
 width
 onabort
 onerror
 onload
 onmousedown
 onmouseup
 referrer
 title
 URL
 all[string IdOfElement]
 anchors[int numberOfATagWithNameDefined]
 name
 offsetLeft
 offsetParent
 offsetTop
 x
 y
 applets[int numberOfAppletTag]
 nameOfPublicProperty
 nameOfPublicMethod()
 embeds[nameOrNumberOfEmbeddedObject]
 height
 hidden
 name
 pluginspage
 src
 type
 units
 width
 onload
 nameOfPublicMethod()
 forms[int numberOfForm]
 images[nameOrNumberOfImage]
 layers[string IdOfPositionedElement]
 links[int numberOfATagWithHrefDefined]See Positioned Element
 hash
 host
 hostname
 href
 innerText
 offsetLeft
 offsetParent
 offsetTop
 pathname
 port
 protocol
 search
 target
 text
 x
 y
 onblur
 onclick
 ondblclick
 onfocus
 onmousedown
 onmousemove
 onmouseout
 onmouseover
 onmouseup
 blur()
 focus()
 plugins[numberOfObject]
 onclick
 ondblclick
 onkeydown
 onkeypress
 onkeyup
 onmousedown
 onmousemove
 onmouseout
 onmouseover
 onmouseup
 captureEvents(Event.EventType)
 close()
 getElementById(string IdOfElement)
 open()
 write(string content)
 writeln(string content)
o event
o Event
 CLICK
 DBLCLICK
 KEYDOWN
 KEYPRESS
 KEYUP
 MOUSEDOWN
 MOUSEMOVE
 MOUSEOUT
 MOUSEOVER
 MOUSEUP
o Function
o history
 back()
 forward()
 go(int numerToJump)
o Image
o innerHeight
o innerWidth
o length
o location
 hash
 host
 hostname
 href
 pathname
 port
 protocol
 search
 reload([bool forceFullReload])
 replace(string location)
o Math
 E
 LN2
 LN10
 LOG2E
 LOG10E
 PI
 SQRT1_2
 SQRT2
 abs(number n)
 acos(number n)
 asin(number n)
 atan(number n)
 atan2(number n,number k)
 ceil(number n)
 cos(number n)
 exp(number n)
 floor(number n)
 log(number n)
 max(number a,number b,number c,.....)
 min(number a,number b,number c,.....)
 pow(number n,number k)
 random()
 round(number n)
 sin(number n)
 sqrt(number n)
 tan(number n)
o name
o navigator
 appCodeName
 appName
 appVersion
 language
 platform
 userAgent
 userLanguage
 mimeTypes[nameOrNumberOfMIMEType]
 description
 enabledPlugin
 suffixes
 type
 plugins[nameOrNumberOfPlugin]
 description
 filename
 length
 name
 nameOrNumberOfMIMEType
 javaEnabled()
 taintEnabled()
o Number
 MAX_VALUE
 MIN_VALUE
 NaN
 NEGATIVE_INFINITY
 POSITIVE_INFINITY
o Object
o opener
o Option
o outerHeight
o outerWidth
o pageXOffset
o pageYOffset
o parent
o RegExp
 $1 ... $9
 input
 lastMatch or $&
 lastParen or $+
 leftContext or $`
 rightContext or $'
o screen
 availHeight
 availWidth
 colorDepth
 height
 pixelDepth
 width
o status
o String
 fromCharCode(number asciiCharacterValue)
o frames[nameOrNumberOfFrame]
 name
o onblur
o onerror
o onfocus
o onload
o onunload
o alert(object)
o blur()
o clearInterval(interval object)
o clearTimeout(timeout object)
o close()
o confirm(string message)
o escape(string textToURLEncode)
o eval(string scriptToEvaluate)
o focus()
o isFinite(number numberToCheck)
o isNaN(number numberToCheck)
o moveBy(int xOffset,int yOffset)
o moveTo(int xPos,int yPos)
o open(string url[,string targetName[,string options[,bool replaceHistoryEntry]]])
o parseInt(string textContainingAnInteger[, int radix])
o parseFloat(string textContainingAFloat)
o print()
o prompt(string message,string defaultValue)
o resizeBy(int xOffset,int yOffset)
o resizeTo(int xWidth,int yWidth)
o scrollBy(int xOffset,int yOffset)
o scroll or scrollTo(int xPos,int yPos)
o setInterval(string or function scriptToEvaluate,int timeInMilliseconds)
o setTimeout(string or function scriptToEvaluate,int timeInMilliseconds)
o unescape(string textToURLUnEncode)
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 4 th 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.

• PositionedElement
o bgColor
o clip
 bottom
 left
 right
 top
o document
o id
o innerHTML
o left
o name
o style
 background
 backgroundColor
 clip
 color
 height
 left
 pixelHeight
 pixelLeft
 pixelTop
 pixelWidth
 top
 visibility
 width
 zIndex
o top
o visibility
o zIndex
o onclick
o ondblclick
o onkeydown
o onkeypress
o onkeyup
o onmousedown
o onmousemove
o onmouseout
o onmouseover
o onmouseup
o captureEvents(Event.EventType)
o resizeTo(int width,int height)

Event objects

• window.event or first argument passed to handler function


o altKey
o button
o clientX
o clientY
o ctrlKey
o keyCode
o modifiers
o pageX
o pageY
o screenX
o screenY
o shiftKey
o target
o this
o type
o srcElement
o which
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
• Array([int length]) (not in JavaScript 1.2)
Array([element 0[, element 1[, element ... n]]])
[element 0, element 1, element ... n]
o length
o concat(elementToAdd[,elementToAdd[,elementToAdd[,etc.]]])
o join([string separatorToReplaceComma])
o pop()
o push(elementToAppend)
o reverse()
o shift(elementToAppend)
o slice(int offsetFromStart[,int offsetToEnd])
o sort([function sortFunction])
o splice(int offsetFromStart,int
numberToRemove[,elementToAdd[,elementToAdd[,etc.]]])
o toString() or valueOf()
o unshift()

Boolean
• Boolean([bool value])
true or false
o toString()
o valueOf()
Date

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

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

• Date()
Date(int UNIXTimestampMilli)
Date(year, month, date[, hours[, minutes[, seconds[,ms]]]])
o getDate()
o getDay()
o getFullYear()
o getHours()
o getMilliseconds()
o getMinutes()
o getMonth()
o getSeconds()
o getTime()
o getTimezoneOffset()
o getUTCDate()
o getUTCDay()
o getUTCFullYear()
o getUTCHours()
o getUTCMilliseconds()
o getUTCMinutes()
o getUTCMonth()
o getUTCSeconds()
o getYear()
o setDate(int dayOfMonth)
o setFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]])
o setHours(int hours[,int minutes[,int seconds[,int milliseconds]]])
o setMilliseconds(int milliseconds)
o setMinutes(int minutes[,int seconds[,int milliseconds]])
o setMonth(int month[,int dayOfMonth])
o setSeconds(int seconds[,int milliseconds])
o setTime(int UNITXTimestampMilli)
o setUTCDate(int UNITXTimestampMilli)
o setUTCFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]])
o setUTCHours(int hours[,int minutes[,int seconds[,int milliseconds]]])
o setUTCMilliseconds(int milliseconds)
o setUTCMinutes(int minutes[,int seconds[,int milliseconds]])
o setUTCMonth(int month[,int dayOfMonth])
o setUTCSeconds(int seconds[,int milliseconds])
o setYear(int numberOfYearsSince1900)
o toUTCString or toGMTString()
o toString or toLocaleString()
o valueOf()

Function
• Function([string varName,[string varName2,[etc.]]]string scriptToEvaluate)
function functionName(listOfVariables) { function code }
functionName = function (listOfVariables) { function code }
o caller
o prototype
o this
o arguments[]
 callee
o apply(object: thisObject[,array: arguments])
o call(object: thisObject[,argument[,argument[,etc.]]])
o toString()
o valueOf()

Image
• Image([int width,int height])

Number
• Number(numberOrString value)
number in hex, octal or decimal
o toFixed(precision)
o toPrecision(precision)
o toExponential(precision)
o toLocaleString()
o toString()
o valueOf()

Object
• Object()
{ propertyName1: value1[, propertyName2: value2[, etc.]] }
o constructor
o hasOwnProperty(string propertyName)
o toString()
o valueOf()

Option
• Option([string text,string value[,bool selected]])

Regular Expression
• RegExp(string pattern,string options)
/pattern/options
o compile(string pattern,string options)
o exec(string stringToMatch)
o global
o ignoreCase
o lastIndex
o source
o test(string stringToMatch)

String
• String([stringOrObjectToBeRepresentedAsString])
'content'
"content"
o length
o anchor(string nameOfAnchor)
o big()
o blink()
o bold()
o charAt(int index)
o charCodeAt(int index)
o concat(string stringToAppend[,string stringToAppend[,string stringToAppend
etc.]])
o fixed()
o fontcolor(string colorValue)
o fontsize(int size)
o indexOf(string searchString[,int offset])
o italics()
o lastIndexOf(string searchString[,int offset])
o link(string hrefToLinkTo)
o match(RegExp searchExpression)
o replace(RegExp searchExpression,string replacementText)
o search(RegExp searchExpression)
o slice(int offsetFromStart[,int offsetFromEnd])
o small()
o split(RegExp separator[,int maxArrayLength])
o strike()
o sub()
o substr(int startIndex[,int length])
o substring(int startIndex,int endIndex)
o sup()
o toLowerCase()
o toUpperCase()
o valueOf()
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.

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

'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
thewindow 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 windowobject. 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:

• parent.frames.otherframename
• parent.frames['otherframename']
• parent.frames[2] (assuming 2 is the index of the frame)
• parent.otherframename

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.

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 form is put inside a positioned
element. See the section on Element contents for more details.

To reference an image, use any of these:

• document['imagename']
• document.images['imagename']
• document.images[number_of_image]

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 form is put inside a positioned
element. See the section on Element contents for more details.

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:

• W3C DOM
• Netscape layers compliant
• Netscape alternative compliant - strictly speaking, this is also layers compliant
• Proprietary DOM

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

• Internet Explorer will only get the values right after the page has loaded, until then it returns incorrect values,
usually 0,0.
• Safari 1.3- gets the offset wrong for fixed position elements, as it adds on the 8 pixel body margin.
• WebTV, Escape, NetFront 3.3-, Tkhtml Hv3 and OmniWeb 4.2- do not provide a way to work out the
position of elements (for a brief while, Escape 4.8 could).
• Netscape 4 can only work out the positions of links, not other elements.
• Netscape 4 will not provide the x and y properties as children of the link object if the name attribute is set.
Instead, they will be children of the corresponding anchor object.
• Opera 8- can have trouble working out positions if the BODY or HTML element has auto left/right margins,
ordisplay:table.
• I am unsure of the capabilities of NetBox, iPanel MicroBrowser and OpenTV here.

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, theelementObject.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 offsetLeftproperties, giving its position relative to its offsetParent, and so
the cycle continues, until theoffsetParent is the topmost node in the document, which will always
be at offset (0,0). We can access
theoffsetParent 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 andlinkObject.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 andoffsetLeft 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


• Opera doubles the scrolling offsets for inline elements, but is reliable for block elements.

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 theoffsetLeft 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 notrecommend 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. This 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 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 orcreate 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. 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:

• The Internet Explorer (Win) engine is used in Maxthon, Avant, many AOL versions, and
many others.
• The Gecko engine is used in Camino, K-Meleon, Flock, and many others.
• The Opera engine is used in a large number of devices in a variety of different applications.
• The KHTML/Webkit engine is used in OmniWeb and Shiira.

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, Konqueror 3+, OmniWeb 4.5+)

Uses document.getElementById (Safari 3 also secretly uses document.all)

Intermediate browsers
(Internet) Explorer 5 (Mac)

Uses document.getElementById and document.all

iCab 3

Uses document.getElementById and 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 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.

NetFront

Uses document.getElementById

Pocket Internet Explorer

Uses document.all

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 really 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

• iCab 2- does not support positioning, but it can still reference the unpositioned element.

To reference a positioned element, there are several ways. The official one is a standard called the
W3CDOM. It is supported well enough by almost 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 simpleif statement to check for the existence of this method.

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 is 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

• iCab 2- does not support visibility.


• OmniWeb 4.2- frequently makes mistakes with visibility.
• Many device browsers will ignore visibility when in small screen reformatting mode.

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


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

• iCab 2- does not support positioning.


• OmniWeb 4.2- does not understand relative positioning and makes mistakes with absolute positioning.
• WebTV can only change horizontal position, not vertical.
• Clue browser sometimes only allows horizontal position to be changed, and sometimes does not allow
positions to be changed at all.

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 andtop 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

• Old Gecko browsers have bugs that are easily avoided. See my Gecko engine mouseout bug
documentation.
• WebTV and OmniWeb 4.2- do not allow you to change 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';

Test it here: change the background colour of this element.

Changing the z-index

• iCab 2- does not support positioning so cannot support z-index.


• WebTV and Clue browser do not support z-index.

The z-index of positioned elements defines what order they should be stacked above each other.
Thez-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.

Element 1.
Increase its z-index.
Element 2.
Increase its z-index.

Changing the clipping

• Opera 6-, Konqueror 3.0-, OmniWeb 4.2-, WebTV, NetFront 3.3-, iCab 2- and Tkhtml Hv3 do not support
clipping (iCab 2- does not support positioning so cannot support clip).
• Internet Explorer 7- does not follow current standards when using "standards rendering mode", and needs
special attention, as shown below.
• In Opera 7-7.1 (fixed in later releases), the background colour shows through the clipping on clipped
elements. The solution is to clip one element and hold a coloured element inside it.
• Although Escape 4.x supports the style object, it does not support the style.clip syntax.

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 ifmyReference.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:

• rect(0px,250px,20px,0px)
• rect(10px,250px,30px,0px)
• rect(20px,250px,40px,0px)

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

• WebTV cannot change element size.


• Escape/Evo 5 will produce errors because I check for document.childNodes.
• ICEbrowser will change element size, but may not show any change until the browser window is resized.
• iCab 2- produces strange overlaps when changing size because it does not support positioning.
• In Clue browser, the element may just disappear.

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:

• 20 x 20
• 30 x 40
• back to 50 x 50

Test
Test

Rewriting the contents

• Opera 6- and OmniWeb 4.2- cannot rewrite the contents of positioned elements.
• WebTV may just crash.
• Layers browsers can only rewrite the contents of absolutely positioned elements.
• Netscape 4 loses style in positioned elements when their contents are rewritten.
• In Clue browser, the element may just disappear, along with the document below it.

In the following example, myReference is obtained as before. Most browsers provide


the innerHTMLproperty, 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 thedocument 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

• Opera 6- and OmniWeb 4.2-, NetFront 3.2- and Clue browser cannot create positioned elements once the
page has loaded.
• WebTV will replace the entire content of the parent element as it incorrecty always reads innerHTML as a
blank string.
• iCab 2- cannot position the element and should create them as unpositioned elements.

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

• Opera 5, OmniWeb 4.2- and Escape/Evo cannot change the background colour of the document (Escape
will change the background colour, but only at the very end of the document, after all of the content).
• Opera 6 and Clue browser will only change the colour behind the content, the original background colour will
remain where the content is smaller than the window.
• NetFront 3.3- will allow you to set the colour to '', but will not show any change until you scroll.

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:

• #f6f6f6
• #d2cbff
• #777777
• '' (returns to default) - not allowed by some old browsers

Changing the display style of any element

• WebTV does not support the display style.


• Warning: Netscape 4, Escape 4, Opera 6-, OmniWeb 4.2-, NetFront 3.2- and Clue browser support the
display: none; style, but do not allow JavaScript to change it. See the guidelines below.
• iCab 2- sometimes makes mistakes with rendering when changing the display style and interprets 'block' as
'return to default'.
• Many device browsers will ignore the display style when in small screen reformatting mode.
• Internet Explorer 4 on Windows does not understand inline display.
• Escape 4 does actually allow you to change the display style (only block, inline and none accepted) using
the style object, but only on layers and ilayers. Since the best use of the display style is for unpositioned
elements, and considering that changing from none to inline or block has no effect in Escape until the window is
resized, i do not recommend even attempting it.

NOTE: most major 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;). Some also support many other display styles. However, not all browsers 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.

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 ordocument.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:
• '' (default)
• 'inline'
• 'block'
• 'none'

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 very much any more.

Layers browsers like Netscape 4 are different to all other browsers. They give each positioned
element a newdocument 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 'wysiwyg' reference-all function. 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 by their nature, 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

• Clue browser can only work out window width.


• Tkhtml Hv3 has the body/documentElement clientHeight/Width values reversed - versions before September
2007 also do not support innerHeight/Width, so they cannot work with this script.

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 importantly, not Internet Explorer.

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

documen
document.
window. t.
documentEle
Browser innerHei body.
ment.
ght clientHei
clientHeight
ght

Opera 9.5+
window document window
strict
Properties and what they relate to

documen
document.
window. t.
documentEle
Browser innerHei body.
ment.
ght clientHei
clientHeight
ght

Opera 9.5+
window window document
quirks

Opera 7-9.2 window window document

Opera 6 window window N/A

Mozilla strict window document window

Mozilla
window window document
quirks

KHTML window document document

Safari window document document

iCab 3 window document document

iCab 2 window window N/A

IE 6+ strict N/A document window

IE 5-7 quirks N/A window 0

IE 4 N/A window N/A

ICEbrowser window window document

Tkhtml Hv3 window window document

Netscape 4 window N/A N/A


As you can see, the browsers seem to have settled on at least one reliable property; innerHeight.
Internet Explorer is the one that cannot make up its mind, and its influence means that other
browsers change theirclientHeight behaviour in different versions in order to match it. For now,
almost all browsers providewindow.innerHeight/Width so that can be used. Internet Explorer
has chosen to swap the values around when it is in strict mode. Fortunately, it gives 0 in quirks
mode. This means that if we see a value on thedocumentElement's properties, and the browser
does not provide the properties on the window object, we can assume it is Internet Explorer in strict
mode.

The most accurate method I could come up with uses the following algorithm, although it may have
problems with future browsers, a situation which I am definitely not happy with:

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+ in 'standards compliant 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). I do not
cover any way to work with these differences.

Finding how far the window has been scrolled

• OmniWeb 4.2-, NetFront 3.3- and Clue browser do not provide any way to do this.
• Safari and OmniWeb 4.5+ have bugs that do not affect this script - see below.

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 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, providedocument.documentElement.scrollLeft/Top.

If the scrollLeft/Top properties are provided on either


the document.body ordocument.documentElement, they are reliable in everything except Safari
and OmniWeb 4.5+, which return -8 if the scrolling is 0, but get all other scrolling offsets correct.
However, as they correctly providewindow.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 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'.

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 hardly used now, you may want the script to work in Netscape 4, since it is capable of
doing this. 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 he
simplest code used to listen for a keyup event would just be this:

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

• Various browsers may restrict which non-printing keys fire what events. Safari 3.1+ in particular will not fire
the keypressevent for any non-printing keys, which in many cases (such as the Esc key) makes it incompatible
with all other browsers.
• Konqueror 3.3-, Safari 1.0 and NetFront 3.3- can only detect key events on text boxes.
• iCab 3- only passes key code information in input boxes.
• Blazer (a version of NetFront) detects limited key events (due to the keyboard handling on the device) and
returns nonsense key codes (such as -1987304).
• WebTV and Escape 4 can only detect key events on text boxes, and may be a little unreliable.
• OmniWeb 4.2- and Opera 5 for Mac do not pass key code information.
• Netscape 4 on Linux does not detect any key events properly.
• Clue browser and Tkhtml Hv3 cannot detect key events.
• I am unsure of the capabilities of NetBox, iPanel MicroBrowser and OpenTV here.

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 gives a different key code for the letter 'a' to all other browsers. It may be more useful to
useString.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

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

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

• In Internet Explorer, the mouse position will be offset by the thickness of the window border (which can
detect mouse movements as if it were the page body). On Windows XP, this would typically be 2 pixels for the
default and classic themes (meaning when the mouse is at 0,0 it will be reported as 2,2), and 0px in fullscreen
mode, but it can be different for other themes or operating systems. There is no known workaround for this bug.
• iCab 2- cannot detect mouse events over the page itself, only over specific elements within it (such as links).
• WebTV only detects mouse events over links.
• OmniWeb 4.2- does not provide any information about events.
• Clue browser only detects mousedown/up events, and only over links.
• I am unsure of the capabilities of NetBox, iPanel MicroBrowser and OpenTV here.

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

Most browsers provide both pageX/Y and clientX/Y. Internet Explorer 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

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

• Konqueror, Safari on Mac, OmniWeb 4.5+, iCab 3+, Tkhtml Hv3 and Escape 4 do not detect right clicks
using normal mouse events.
• Opera users can choose to allow scripts to detect right clicks - enabled by default since Opera 10.
• Internet Explorer 4-, Safari 1.1-, Opera, Konqueror, ICEBrowser, NetFront, Tkhtml Hv3+, Netscape 4,
Escape, OmniWeb 4.2-, WebTV/MSNTV and Clue browser do not support the oncontextmenu event.
• WebTV, iCab 2-, NetFront 3.3- and Clue browser do not pass information about mouse buttons.
• WebTV cannot detect mousedown or mouseup events.
• OmniWeb 4.2- does not provide any information about events.
• I am unsure of the capabilities of NetBox, iPanel MicroBrowser and OpenTV here.

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 a few 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 (and is also not very well supported).

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

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

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 many 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.
Creating objects

• Netscape 4, IE 4 on Windows and Mac, and IE 5 on Mac do not support the instanceof operator.

Any function in JavaScript can be used to create custom object classes, simply by calling it using the
keywordnew. 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:

• mything.containedValue
• mything.othercontainedValue
• mything.anothercontainedValue

You could also now write this:

myobject.prototype.newContainedValue = someValue;

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

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:

• this.retArea = getTheArea;
• this.mvBy = mvCclBy;
• this.retCirc = function () { ... };

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


• Internet Explorer 5.2-, Safari 1.3-, Konqueror 3.3-, Netscape 4, and WebTV do not implement
the hasOwnProperty method.

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 hasOwnPropertythat 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 themycircle 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
themysphere 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 themycircle 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 themycircle.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 mysphereprototype 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 themysphere 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 themycircle 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 PrivateConstructorobject. 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.
THE SINGLETON DESIGN PATTERN IN JAVASCRIPT
This singleton pattern, despite the controversy surrounding it, is definitely one of the most popular GoF
patterns. While I agree that using singletons to make global variables look like OOP is bad, the pattern
actually has quite a few "real" uses. A few months ago, while I was working on my Ajax framework, I was
looking for a way to ensure that the object I'm using for asynchronous communication could only be
instantiated once, and that an instance is only created when it's actually needed. A quick google for
"JavaScript singleton" returned quite a few pages. Here's the solution that most of them had come up
with:

Click anywhere in the code to remove line numbers.


1 var singleton = new function() {
2 var rand = Math.round(Math.random() * 100);
3 this.getRand = function() {
4 return rand;
5 }
6 }

In order to obtain a reference to the singleton instance, you'd just use the global variable singleton. Close,
but no cigar. First of, look at this code:

Click anywhere in the code to remove line numbers.


1 var secondSingleton = new singleton.constructor;
2 document.write(singleton.getRand() + "\r\n");
3 document.write(secondSingleton.getRand());

Using the constructor attribute of the singleton object which holds a reference to the anonymous
constructor function I can easily create a second instance of the "singleton". The code snippet above will
therefore give you two different outputs, one for the original instance and one for the second instance.
Two instances = no singleton. Of course circumventing the one-instance limitation required some
"hacking" and therefore doesn't necessarily invalidate this solution. The real problem is that a singleton
has a property that this solution doesn't take into account: an instance of a singleton is only created, when
a special method (typically called getInstance) is invoked. The above solution will create an instance of
the singleton regardless whether its actually needed or not. If the singleton allocates a lot of resources,
executes a bunch of complex code or even does communication via Ajax in its constructor, then this
behavior could be a problem.

In order to come up with a solution, I looked at how singletons are typically implemented in other
languages like Java:

Click anywhere in the code to remove line numbers.


1 class Singleton {
2 private static Singleton instance = null;
3 private Singleton() { }
4 public static Singleton getInstance() {
5 if (instance == null) {
6 instance = new Singleton();
7 }
8 return instance;
9 }
10 }

For singletons to work in JavaScript, I needed a way to do private constructors and both private and
public static members, two concepts that JavaScript doesn't natively support. As ever so often, the
solution was to use a closure to make the instance attribute and the constructor private. Here's the
solution I came up with:

Click anywhere in the code to remove line numbers.


1 var Singleton = (function() {
2 var instance = null;
3
4 function PrivateConstructor() {
5 var rand = Math.round(Math.random() * 100);
6 this.getRand = function() {
7 return rand;
8 }
9 }
10
11 return new function() {
12 this.getInstance = function() {
13 if (instance == null) {
14 instance = new PrivateConstructor();
15 instance.constructor = null;
16 }
17 return instance;
18 }
19 }
20 })();
21
22 var singletonInstance = Singleton.getInstance();

The trick here is to define an anonymous function and call it immediately. The function creates a new
variable scope in which we define a local variable "instance". The function then returns a new object that
provides a privileged method "getInstance". Since this method holds a reference to the instance variable,
a closure is formed and the local variable becomes a private static member. The actual constructor
function is defined within the anonymous function and hence becomes inaccessible from the outside.
Since getInstance also holds a reference to that function, another closure is formed allowing getInstance
to create new instances using PrivateConstructor.

In a remotely aspect oriented kind of way, you could even do this:

Click anywhere in the code to remove line numbers.


1 var global = this;
2
3 function singletonify(constructorName) {
4 var constructorFunc = global[constructorName];
5 var instance = null;
6
7 global[constructorName] = new function() {
8 this.getInstance = function() {
9 if (instance == null) {
10 instance = new constructorFunc();
11 instance.constructor = null;
12 }
13 return instance;
14 }
15 }
16 }
17
18 function RegularConstructor() {
19 var rand = Math.round(Math.random() * 100);
20 this.getRand = function() {
21 return rand;
22 }
23 }
24
25 singletonify("RegularConstructor");
26
27 var myInstance = RegularConstructor.getInstance();
28 document.write(myInstance.getRand());

This will turn an existing constructor function into a singleton. The name of the constructor needs to be
passed as a string to the singletonify function, because otherwise it wouldn't be able to overwrite the
orignal constructor.

I hope someone find this useful. Please let me know what you think.

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, most browsers have a minimum delay length of between 25 and 75 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, the timeout will start a new thread, and the code specified in the call to
setTimeout will be run in the new thread, along side any code that is 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 could be executing at any time next to another thread. Many JavaScript engines 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.
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.
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.

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.

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. Thedocument.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. Useif(!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.

• JavaScripts cannot read or write files on users' computers. They cannot create files on the
server (except by communicating with a server side script that creates files for them). The only
thing they can store on the user's computer are cookies.
• They are allowed to interact with other pages in a frameset, if those frames originate from the
same Web site, but not if they originate from another Web site (the postMessage method
from HTML 5 does safely extend this capability, but I will not cover that here). Some browsers
will even treat different port numbers on the same server as a different Web site.
• JavaScript cannot be used to set the value attribute of a file input, and will not be allowed to
use them to upload files without permission.
• JavaScript cannot read what locations a user has visited by reading them from the location
object, although it can tell the browser to jump back or forward any number of steps through the
browser history. It cannot see what other Web pages the user has open.
• JavaScript cannot access the cookies or variables from other sites.
• It cannot see when the user interacts with other programs, or other parts of the browser
window.
• It cannot open windows out of sight from the user or too small for the user to see, and in
most browsers, it cannot close windows that it did not open.

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.

Encrypting data before it is sent to you

Normally, this cannot be done with JavaScript using the Internet alone. You can 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 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 onhttp://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:

• Use a throw-away email address like a yahoo or hotmail account when posting to
newsgroups, signing online guestbooks, or writing your email address on your Web pages. That
way, when you start to get too much spam on that email address, you can just dispose of that
email account, and get a new one.
• If you can, tell your email client (program) not to send read-confirmations when you read
your emails. This way your email client does not automatically confirm your email address.
• Be careful when setting up auto-replies.
• When you post your email address, change it to read something
likemyName@REMOVE_THISmydomain.com or myName(replace with @
symbol)mydomain.com and hope that anyone who legitimately replies to it works out what they
need to do to turn it back into a proper email address. The problem is that not all of them
understand this, and don't understand why the email adress does not just work. So, you can try
the next point as well:
• Use JavaScript. How? Read on!
Using JavaScript to write your email address

I have never heard of an email harvester that is clever enough to interpret JavaScript. All they can
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:

myEmailName@myEmailDomain.com
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:

• var myvariable = 'sugar';


• document.myform.submit();
• document['myimage'].src = myStoredImage.src;

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

• if( x < 5 ); {

• x++;
• }

• if( x < 5; ) {

• x++;

• }

• if( x < 5 ) {;

• x++;

• }

• if( x < 5 ) {

• x++;

• };

• if( x < 5 ) {

• x++;;

• }

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'

innerHTML

' was '

alert

myP
+

'here'

'there'

</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 4 th generation
browsers, and move on to the much more advanced DOM browsers (also known as 5th generation
browsers).

Contents

• Standard document components - all addressable components of a web page.


• Positioned elements - all addressable components of an absolutely positioned div.
• Event objects - all accessible information about an event.
• Intrinsic objects - variable types with constructors.

Key

• Parent object
o Child object
o Child property
o Child object being accessed through a collection[]
o Event
o Method()

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, calledvarName 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

• window or self
o Array
o Boolean
o closed
o Date
 parse(string dateAsAString)
o defaultStatus
o document
 bgColor
 body
 clientHeight
 clientWidth
 scrollLeft
 scrollTop
 style
 background
 backgroundColor
 color
 cookie
 documentElement
 clientHeight
 clientWidth
 scrollLeft
 scrollTop
 style
 background
 backgroundColor
 color
 domain
 fgColor
 IdOfPositionedElement
 lastModified
 nameOfForm
 action
 encoding
 length
 method
 name
 nameOfInput/textarea/select or nameOfInputsSharingName[int
index]
 checked
 defaultChecked
 defaultValue
 disabled
 form
 length
 name
 selectedIndex
 type
 value
 options[int numberOfOptionWithSelectInput]
 defaultSelected
 index
 selected
 text
 value
 onblur
 onchange
 onclick
 ondblclick
 onfocus
 onkeydown
 onkeypress
 onkeyup
 onmousedown
 onmouseup
 blur()
 focus()
 select()
 target
 elements[int numberOfInput]
 onreset
 onsubmit
 reset()
 submit()
 nameOfImage
 border
 complete
 height
 lowsrc
 name
 src
 width
 onabort
 onerror
 onload
 onmousedown
 onmouseup
 referrer
 title
 URL
 all[string IdOfElement]
 anchors[int numberOfATagWithNameDefined]
 name
 offsetLeft
 offsetParent
 offsetTop
 x
 y
 applets[int numberOfAppletTag]
 nameOfPublicProperty
 nameOfPublicMethod()
 embeds[nameOrNumberOfEmbeddedObject]
 height
 hidden
 name
 pluginspage
 src
 type
 units
 width
 onload
 nameOfPublicMethod()
 forms[int numberOfForm]
 images[nameOrNumberOfImage]
 layers[string IdOfPositionedElement]
 links[int numberOfATagWithHrefDefined]See Positioned Element
 hash
 host
 hostname
 href
 innerText
 offsetLeft
 offsetParent
 offsetTop
 pathname
 port
 protocol
 search
 target
 text
 x
 y
 onblur
 onclick
 ondblclick
 onfocus
 onmousedown
 onmousemove
 onmouseout
 onmouseover
 onmouseup
 blur()
 focus()
 plugins[numberOfObject]
 onclick
 ondblclick
 onkeydown
 onkeypress
 onkeyup
 onmousedown
 onmousemove
 onmouseout
 onmouseover
 onmouseup
 captureEvents(Event.EventType)
 close()
 getElementById(string IdOfElement)
 open()
 write(string content)
 writeln(string content)
o event
o Event
 CLICK
 DBLCLICK
 KEYDOWN
 KEYPRESS
 KEYUP
 MOUSEDOWN
 MOUSEMOVE
 MOUSEOUT
 MOUSEOVER
 MOUSEUP
o Function
o history
 back()
 forward()
 go(int numerToJump)
o Image
o innerHeight
o innerWidth
o length
o location
 hash
 host
 hostname
 href
 pathname
 port
 protocol
 search
 reload([bool forceFullReload])
 replace(string location)
o Math
 E
 LN2
 LN10
 LOG2E
 LOG10E
 PI
 SQRT1_2
 SQRT2
 abs(number n)
 acos(number n)
 asin(number n)
 atan(number n)
 atan2(number n,number k)
 ceil(number n)
 cos(number n)
 exp(number n)
 floor(number n)
 log(number n)
 max(number a,number b,number c,.....)
 min(number a,number b,number c,.....)
 pow(number n,number k)
 random()
 round(number n)
 sin(number n)
 sqrt(number n)
 tan(number n)
o name
o navigator
 appCodeName
 appName
 appVersion
 language
 platform
 userAgent
 userLanguage
 mimeTypes[nameOrNumberOfMIMEType]
 description
 enabledPlugin
 suffixes
 type
 plugins[nameOrNumberOfPlugin]
 description
 filename
 length
 name
 nameOrNumberOfMIMEType
 javaEnabled()
 taintEnabled()
o Number
 MAX_VALUE
 MIN_VALUE
 NaN
 NEGATIVE_INFINITY
 POSITIVE_INFINITY
o Object
o opener
o Option
o outerHeight
o outerWidth
o pageXOffset
o pageYOffset
o parent
o RegExp
 $1 ... $9
 input
 lastMatch or $&
 lastParen or $+
 leftContext or $`
 rightContext or $'
o screen
 availHeight
 availWidth
 colorDepth
 height
 pixelDepth
 width
o status
o String
 fromCharCode(number asciiCharacterValue)
o frames[nameOrNumberOfFrame]
 name
o onblur
o onerror
o onfocus
o onload
o onunload
o alert(object)
o blur()
o clearInterval(interval object)
o clearTimeout(timeout object)
o close()
o confirm(string message)
o escape(string textToURLEncode)
o eval(string scriptToEvaluate)
o focus()
o isFinite(number numberToCheck)
o isNaN(number numberToCheck)
o moveBy(int xOffset,int yOffset)
o moveTo(int xPos,int yPos)
o open(string url[,string targetName[,string options[,bool replaceHistoryEntry]]])
o parseInt(string textContainingAnInteger[, int radix])
o parseFloat(string textContainingAFloat)
o print()
o prompt(string message,string defaultValue)
o resizeBy(int xOffset,int yOffset)
o resizeTo(int xWidth,int yWidth)
o scrollBy(int xOffset,int yOffset)
o scroll or scrollTo(int xPos,int yPos)
o setInterval(string or function scriptToEvaluate,int timeInMilliseconds)
o setTimeout(string or function scriptToEvaluate,int timeInMilliseconds)
o unescape(string textToURLUnEncode)

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 4 th 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.

• PositionedElement
o bgColor
o clip
 bottom
 left
 right
 top
o document
o id
o innerHTML
o left
o name
o style
 background
 backgroundColor
 clip
 color
 height
 left
 pixelHeight
 pixelLeft
 pixelTop
 pixelWidth
 top
 visibility
 width
 zIndex
o top
o visibility
o zIndex
o onclick
o ondblclick
o onkeydown
o onkeypress
o onkeyup
o onmousedown
o onmousemove
o onmouseout
o onmouseover
o onmouseup
o captureEvents(Event.EventType)
o resizeTo(int width,int height)

Event objects

• window.event or first argument passed to handler function


o altKey
o button
o clientX
o clientY
o ctrlKey
o keyCode
o modifiers
o pageX
o pageY
o screenX
o screenY
o shiftKey
o target
o this
o type
o srcElement
o which

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
• Array([int length]) (not in JavaScript 1.2)
Array([element 0[, element 1[, element ... n]]])
[element 0, element 1, element ... n]
o length
o concat(elementToAdd[,elementToAdd[,elementToAdd[,etc.]]])
o join([string separatorToReplaceComma])
o pop()
o push(elementToAppend)
o reverse()
o shift(elementToAppend)
o slice(int offsetFromStart[,int offsetToEnd])
o sort([function sortFunction])
o splice(int offsetFromStart,int
numberToRemove[,elementToAdd[,elementToAdd[,etc.]]])
o toString() or valueOf()
o unshift()
Boolean
• Boolean([bool value])
true or false
o toString()
o valueOf()

Date

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

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

• Date()
Date(int UNIXTimestampMilli)
Date(year, month, date[, hours[, minutes[, seconds[,ms]]]])
o getDate()
o getDay()
o getFullYear()
o getHours()
o getMilliseconds()
o getMinutes()
o getMonth()
o getSeconds()
o getTime()
o getTimezoneOffset()
o getUTCDate()
o getUTCDay()
o getUTCFullYear()
o getUTCHours()
o getUTCMilliseconds()
o getUTCMinutes()
o getUTCMonth()
o getUTCSeconds()
o getYear()
o setDate(int dayOfMonth)
o setFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]])
o setHours(int hours[,int minutes[,int seconds[,int milliseconds]]])
o setMilliseconds(int milliseconds)
o setMinutes(int minutes[,int seconds[,int milliseconds]])
o setMonth(int month[,int dayOfMonth])
o setSeconds(int seconds[,int milliseconds])
o setTime(int UNITXTimestampMilli)
o setUTCDate(int UNITXTimestampMilli)
o setUTCFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]])
o setUTCHours(int hours[,int minutes[,int seconds[,int milliseconds]]])
o setUTCMilliseconds(int milliseconds)
o setUTCMinutes(int minutes[,int seconds[,int milliseconds]])
o setUTCMonth(int month[,int dayOfMonth])
o setUTCSeconds(int seconds[,int milliseconds])
o setYear(int numberOfYearsSince1900)
o toUTCString or toGMTString()
o toString or toLocaleString()
o valueOf()

Function
• Function([string varName,[string varName2,[etc.]]]string scriptToEvaluate)
function functionName(listOfVariables) { function code }
functionName = function (listOfVariables) { function code }
o caller
o prototype
o this
o arguments[]
 callee
o apply(object: thisObject[,array: arguments])
o call(object: thisObject[,argument[,argument[,etc.]]])
o toString()
o valueOf()

Image
• Image([int width,int height])
Number
• Number(numberOrString value)
number in hex, octal or decimal
o toFixed(precision)
o toPrecision(precision)
o toExponential(precision)
o toLocaleString()
o toString()
o valueOf()

Object
• Object()
{ propertyName1: value1[, propertyName2: value2[, etc.]] }
o constructor
o hasOwnProperty(string propertyName)
o toString()
o valueOf()

Option
• Option([string text,string value[,bool selected]])

Regular Expression
• RegExp(string pattern,string options)
/pattern/options
o compile(string pattern,string options)
o exec(string stringToMatch)
o global
o ignoreCase
o lastIndex
o source
o test(string stringToMatch)
String
• String([stringOrObjectToBeRepresentedAsString])
'content'
"content"
o length
o anchor(string nameOfAnchor)
o big()
o blink()
o bold()
o charAt(int index)
o charCodeAt(int index)
o concat(string stringToAppend[,string stringToAppend[,string stringToAppend
etc.]])
o fixed()
o fontcolor(string colorValue)
o fontsize(int size)
o indexOf(string searchString[,int offset])
o italics()
o lastIndexOf(string searchString[,int offset])
o link(string hrefToLinkTo)
o match(RegExp searchExpression)
o replace(RegExp searchExpression,string replacementText)
o search(RegExp searchExpression)
o slice(int offsetFromStart[,int offsetFromEnd])
o small()
o split(RegExp separator[,int maxArrayLength])
o strike()
o sub()
o substr(int startIndex[,int length])
o substring(int startIndex,int endIndex)
o sup()
o toLowerCase()
o toUpperCase()
o valueOf()
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:

• Opera 7+
• Mozilla Gecko (Mozilla, Firefox, Netscape 6+)
• KHTML/WebKit (Konqueror 2+, Safari, OmniWeb 4.5+)
• Internet Explorer 5+
• iCab 3+ (2- is passive only)
• ICEbrowser
• Escape/Evo 5 (partial only [and broken])
• Tkhtml Hv3 (partial only [and broken])
• NetFront 3+ (partial only)
• Netgem 4 browser (NetBox) - not tested
• OpenTV - not tested
• iPanel MicroBrowser with advanced modules - not tested

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. Some of these browsers are still in use, and
should not be discounted. It is important 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. The
Windows version supports half of the DOM 1 modules, and parts of a few DOM 2 modules. The Mac
version supports slightly more of DOM 1. Opera and Mozilla 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. KHTML/WebKit is not far behind with complete DOM 1 and almost complete DOM 2. iCab
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 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 hopefully they will improve its DOM support in the near
future, so that it can handle these scripts.

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, childNodesand attributes are collections, the title='The test paragraph' pair is an
attribute node, and the text strings are text nodes.

Referencing the element nodes

• Konqueror incorrectly requires the getElementsByTagName parameter to be in lower case when using
XHTML strict doctypes, but served as text/html.
• iCab 3 fails to keep the object returned by getElementsByTagName updated.
• Tkhtml Hv3 adds all text nodes that appear before the opening <body> tag, as separate text nodes into the
body'schildNodes collection.
• Escape/Evo 5 fails to use the childNodes collections, and will abort the script. Ignore this browser.
• Early IE5 Mac did not provide the childNodes.length property. This was fixed automatically. If you are
worried, use something like this: for( var x = 0; node.childNodes[x]; x++ )
Using the DOM, there are several ways that we could reference the paragraph. We can
usegetElementsByTagName 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, getElementsByTagNamebecomes 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)
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 thechildNodes 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'schildNodes 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 getElementsByTagNamemethod 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

• Tkhtml Hv3 does not support the attributes collection.

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

• NetFront gets the case wrong when retrieving attribute values (align is returned as 'Center' instead of
'center').
• Opera 7-8 will retrieve resolved values instead of specified values for attributes like 'href' and 'src'.
• Many browsers (particularly Internet Explorer 7-) will have trouble retrieving values for style and class, as
well as event handlers.

If all you want to do is to check the value of an attribute, not manually edit its entry, it is easier to just
usegetAttribute.

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

• Internet Explorer 7- (and some minor browsers) cannot set values for style, class or event handlers.
• Opera 7.0-7.1, could not set the align 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).

• NetFront 3.2- cannot move the existing paragraph to a new location (version 3.3 may also fail, if the device
does not have much memory), but it gets the rest of the example right.

Reading and writing problematic attributes

Internet Explorer 7- (and some minor browsers) cannot set values for style, class or event handlers,
usingsetAttribute. Internet Explorer 8 has fixed most of these, but still cannot set event handlers. 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

• Mozilla/Firefox/Netscape 6+ and Opera 9.2x- will split very long text nodes into multiple smaller 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 thefirstChild (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 thenormalize() 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

• KHTML/WebKit Konqueror 3.4-, Safari 1.2- and OmniWeb 4.5-5.0 do not always reflow the page when
changing the text of text nodes.
• Tkhtml Hv3 versions before September 2007 cannot change the value of existing 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.

• NetFront 3.2- cannot move the existing paragraph to a new location (version 3.3 may also fail, if the device
does not have much memory), but it gets the rest of the example right.

Creating new nodes and removing existing ones

• NetFront 3.2- cannot create or insert new nodes (version 3.3 often crashes with scripts that use this, if it is
running on a lower memory device).

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
usesremoveChild 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

• Internet Explorer 5.x on Windows, NetFront 3.3- and Tkhtml Hv3 do not support document fragments.
• Internet Explorer 5 on Mac cannot add text nodes to document fragments, and cannot append the
fragment's contents to a document.

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.

When you click on the link this is the output we get.

Paragraph 1

Paragraph 2

Paragraph 3

Paragraph 4

Paragraph 5

Paragraph 6

Paragraph 7

Paragraph 8

Paragraph 9

Paragraph 10

When we click again we get this. 2 times now since we clicked on it two times.

Paragraph 1

Paragraph 2

Paragraph 3

Paragraph 4

Paragraph 5

Paragraph 6
Paragraph 7

Paragraph 8

Paragraph 9

Paragraph 10

Paragraph 1

Paragraph 2

Paragraph 3

Paragraph 4

Paragraph 5

Paragraph 6

Paragraph 7

Paragraph 8

Paragraph 9

Paragraph 10
DOM tables

• Konqueror, Safari and OmiWeb 4.5+ get the cellIndex property wrong.
• Tkhtml Hv3 only supports table.tBodies, tBodies[].rows, rows[].cells and childNodes, but
none of the other properties shown here.
• Pre-alpha versions of Tkhtml Hv3 only support table.tBodies and childNodes, but none of the other
properties shown here.

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, but most of those designed
to create parts of tables, as well as the one to delete table captions, are not implemented properly in
some browsers, so I will only talk you through those that are the most reliable.

• table
o caption
 childNodes[]
o tHead
 rows[]
 cells[]
 childNodes[]
o tFoot
 rows[]
 cells[]
 childNodes[]
o tBodies[]
 rows[]
 cells[]
 childNodes[]

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 deleteTHead() and deleteTFoot() methods that do exactly what they
say. Each thead, tbody and tfoot also have the deleteRow(rowIndex) method to delete rows.
Each row also has thedeleteCell(cellIndex) method to delete cells. The cells have
the cellIndex property (except in early Konqueror versions) and rows have
the rowIndex property.
Adding a row to a table

• Internet Explorer on Mac has very poor support for creating table elements using the dedicated methods - as
a result, this tutorial avoids those, and uses normal DOM core instead.

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

Adding one cell to every row in a table

• Internet Explorer on Mac has very poor support for creating table elements using the dedicated methods - as
a result, this tutorial avoids those, and uses normal DOM core instead.

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

DOM CSS

Individual element styles

• ICEbrowser and Escape/Evo cannot read or write the float style.


• NetFront 3.5 beta fails throws errors when setting className.

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 styleattribute, 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 (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


• getComputedStyle and currentStyle are not supported by Safari 2-, Konqueror 3.4- or Tkhtml Hv3.

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 thewindow.getComputedStyle method. This is supported by Opera 7+,
Mozilla, iCab 3+, Konqueror 3.5+, and Safari 3+. Internet Explorer also offers an alternative;
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.
SincecurrentStyle 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 are not standardised, 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 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

• This is supported correctly by IE 4+ Win, IE 5+ Mac, Gecko (Mozilla/Firefox/Netscape 6+), and Opera 9+.
• This is supported incorrectly by KHTML/WebKit (Konqueror 3+/Safari/OmniWeb 4.5+) and ICEbrowser.
• This is supported almost correctly by IE 4 Mac.
• This is supported correctly using a different syntax by Opera 7+ and iCab 3.
• This is supported correctly using a different syntax by KHTML/WebKit.
• iCab 3 fails to stop applying generated content and HTML element color from the preferred stylesheet when
it is disabled.

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

• Internet Explorer 4+ Win and 5+ Mac correctly support document.styleSheets.


• Internet Explorer 4 Mac supports document.styleSheets but does not provide the title
attribute. Instead, the title of the style or link element must be
used: document.styleSheets[0].owningElement.title.
• Gecko (Mozilla/Firefox/Netscape 6+) correctly supports document.styleSheets.
• Opera 9+ correctly supports document.styleSheets.
• Konqueror and Safari [KHTML/WebKit] incorrectly only populate
the document.styleSheets collection with stylesheets that are enabled at any particular
instant. The stylesheets cannot be disabled from here, and do not have the title property.
• ICEbrowser populates the collection and allows the stylesheet to be enabled / disabled but
only if the rel attribute is set to 'stylesheet' and not 'alternate stylesheet' - this defies the purpose
of switching stylesheets, and goes against the W3C's HTML specification so I just accept that it
will not handle the script correctly. ICEbrowser users will have to just stick to the default
stylesheet, attepting to change it will leave them only with the persistent stylesheet.
• NetFront provides the collection but it is always empty ...
• Opera 7-8 and iCab 3 do not provide the collection at all.

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.
• Internet Explorer 4+ Win supports this technique, but until the disabled property has been set
with JavaScript, it will show as false, even if the 'rel' attribute is set to 'alternate stylesheet' and
hence the stylesheet is disabled.
• Internet Explorer 4+ Mac correctly supports this technique.
• Gecko (Netscape 6+, Mozilla etc.) correctly supports this technique.
• Opera 9+ correctly supports this technique.
• Konqueror and Safari [KHTML/WebKit] support this technique, but until the disabled property
has been set with JavaScript, it will show as false, even if the 'rel' attribute is set to 'alternate
stylesheet' and hence the stylesheet is disabled.
• Opera 7-8 and iCab 3+ support this technique, but until the disabled property has been set
with JavaScript, it will show as false, even if the 'rel' attribute is set to 'alternate stylesheet' and
hence the stylesheet is disabled. Also, changes made using the view menu will not be reflected.
• ICEbrowser does not support this technique.

You can see that from these points, the following is required:

• Use the link and style tag technique in Opera 7-8 and iCab 3 and make sure that you do not
try to store the user's preference until they have chosen a stylesheet.
• Either of:
o Use the link and style tag technique in Opera 9+.
o Use the document.stylesheets collection in Opera 9+.
• Use the link and style tag technique in KHTML/WebKit and make sure that you do not try to
store the user's preference until they have chosen a stylesheet.
• Either of:
o Use the link and style tag technique in Gecko.
o Use the document.stylesheets collection in Gecko.
• Either of:
o Use the link and style tag technique in IE using document.all.tags to reference them
in IE 4 and make sure that you do not try to store the user's preference until they have
chosen a stylesheet.
o Use the document.stylesheets collection in IE, as this makes it easier to include IE 4
as well - make sure you use the title from the owningElement if the title is not immediately
available.
• Use the document.stylesheets collection in ICEbrowser, but only bother if you are not using
alternate stylesheets.

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+ and Mozilla/Firefox. It is
partially supported by Safari, 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 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, Konqueror and ICEbrowser
for now, and hope that their support improves in the future.

If you need compatibility with Safari, 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
requredocument.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 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 as well. 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 for now, and 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

• Mozilla gives STYLE element stylesheets the address of the current page as their href property, instead of
null.
• Opera 9.2- does not provide the href property for stylesheets added using XML processing instructions,
and they cannot be disabled.
• Safari and Konqueror only populate the list with stylesheets that are enabled at the specific time, meaning
that if you use alternate stylesheets, most of them will be missing from the list. Safari 2- and Konqueror do not
provide the title property, and they do not let you modify the disabled property.
• Safari 3-, Konqueror and ICEbrowser do not include stylesheets added using XML processing instructions
(ICEbrowser does not understand XML).
• Safari and Konqueror will populate the list with stylesheets that are added to the BODY with DOM, but these
will have no styling effects on the document (stylesheets are only allowed in the document HEAD).
• ICEbrowser only populates the list with persistent/preferred stylesheets, meaning that if you use alternate
stylesheets, they will be missing from the list.
• ICEbrowser does not add dynamically generated stylesheets into the collection.
• NetFront provides the collection but it is always empty.

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

• Safari 2- and Konqueror do not provide the sheet property for linked stylesheets.
• ICEbrowser does not provide the sheet property for any stylesheets.

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

• Safari 2- and Konqueror sometimes put an extra comma on the end of the mediaText (making it invalid).
• Safari 3-, Konqueror and ICEbrowser do not allow media types to be added or removed.
• ICEbrowser ignores media attributes, and treats mediaText as a blank string.

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 mediaattribute. 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:

• Append the print media type to all stylesheets on this page.


• The page should appear to have lost all its styling information.
• You can then test if it worked by using your browser's print preview feature.
• If you are using Opera, you can also append the handheld media type to all stylesheets on
this page, and test it using View - Small screen.
• Check what media types the first stylesheet is using.
• You can then delete the print media type and delete the handheld media type.

Stylesheet cssRules collection

• Mozilla does not provide this collection for alternate stylesheets that do not have a title attribute.
• Safari 2- and Konqueror cannot add or remove rules.
• Safari, Konqueror and ICEbrowser ignore some @ rules that they do understand, such as @charset rules.
• ICEbrowser cannot add rules.

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

• Mozilla, Safari, Konqueror and ICEbrowser cannot rewrite rules using cssText.
• Setting rule cssText will cause Opera 9-9.2 to apply the old version of the rule as well as the new one.
• Safari 2- and Konqueror do not provide cssText for the CSS rule object.
• Safari 2-, Konqueror and ICEbrowser remove any namespace prefix from the selector part of the rule,
meaning that it appears to target elements that it does not.
• Safari 2- and ICEbrowser make the selector part of the rule upper case, so it is not valid for XML based
documents.
• Safari 2- corrupts ID, attribute, and class selectors beyond recognition, making them invalid and useless.
• ICEbrowser adds wildcards to the selector part of the rule where wildcards are assumed.
• ICEbrowser changes class selectors to [class~=attribute] selectors.
• The selector part of the rule only includes everything upto the first comma in ICEbrowser.

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 theparentStyleSheet 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


• Mozilla does not allow setting of rule selectorText.
• Mozilla splits some valid styles (such as 'padding') into several invalid styles (such as 'padding-left-
value' and'padding-left-ltr-source: physical;').
• Mozilla adds a strange '*|' namespace prefix to element selectors, but only if there is at least
one @import rule in one of the document's stylesheets, and if there are at least two LINK elements in the
document (at least one of which must be a stylesheet).
• Safari 2- corrupts ID, attribute, and class selectors in the selectorText beyond recognition, making it
invalid and useless.
• Safari 2- always returns null when using the item method on a rule.
• Safari 2- and Konqueror cannot add, remove or change styles from a rule using the methods of the style
object.
• Safari 2-, Konqueror and ICEbrowser cannot rewrite rules using style.cssText.
• Safari 2-, Konqueror and ICEbrowser remove any namespace prefix from the selectorText, meaning that
it appears to target elements that it does not.
• Safari 2- and ICEbrowser make selectorText upper case, so it is not valid for XML based documents.
• ICEbrowser cannot change rule styles using .style.color syntax.
• selectorText only includes everything upto the first comma in ICEbrowser.
• ICEbrowser adds wildcards to the selectorText where wildcards are assumed.
• ICEbrowser changes class selectors to [class~=attribute] selectors.
• ICEbrowser cannot retrieve values of short form styles like 'margin' and can only retrieve values for the
expanded form'margin-left'.
• ICEbrowser returns all pixel values with a decimal point '0.0px'.
• ICEbrowser ignores priorities when setting styles.

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
• Safari, Konqueror and ICEbrowser actually get this one right. I felt it an important enough event to mention
it.

@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
• Safari and Konqueror do not provide the parentRule property for rules in a @media block.
• ICEbrowser does not understand media queries but interprets them as a media block with their tokens
treated as separate media types.

@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 mediaproperty, 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 Internet Explorer

Internet Explorer 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. If you need 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.

The styleSheets list


• Internet Explorer on Mac does not populate the list with stylesheets that are added using DOM after the
page has loaded.
• The media property is read-only in Internet Explorer on Mac.
• Setting media to 'print' followed by '' causes Internet Explorer on Windows to start using print media
instead of screen media for normal Web page viewing.

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.

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 on Mac cannot add or remove rules.

Internet Explorer provides the rules collection, in the same way as standards compliant browsers
usecssRules. 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 rulescollection 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
thecssRules collection is too short). For this reason, it is important to check for the rules collection
first, and use that if it is available, then use the standard cssRules collection if not.

var oSheet = document.styleSheets[0];

var oRule = oSheet.rules ? oSheet.rules[3] : oSheet.cssRules[7];

Adding and removing rules in Internet Explorer (on Windows) is done using
the addRule and removeRulemethods. 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


• selectorText only includes everything upto the first comma in IE.
• selectorText adds wildcards for selectors where wildcards are assumed in IE.
• cssText contains the {} braces in IE mac (it also includes them for Element.style.cssText).
• The style.item(0) method returns '}' in IE mac if the rule does not have any styles that it recognises.
• Elements and style names in selectorText and cssText are upper case in IE (meaning that they are not
valid for XML based documents).
• selectorText is read-only in IE Mac.

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 theparentStyleSheet property.

They also have the all important style property. However, here there are some significant
differences. Theitem 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, 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 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. I will give details later on how to
get support for Internet Explorer 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

• ICEbrowser sometimes fails to detect the page's load event.


• ICEbrowser will forget to remove listeners if you reload, so the effect is cumulative.
• Tkhtml Hv3 versions before September 2007 fire events only in the bubbling order, but fire both the bubbling
and capturing listeners (in that order).

The DOM 2 events module was designed to cope with all possibilities, so not only could you add
multiple event handlers to a single event, but you could also say in what order they 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 withreferenceToElement.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, 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 and ICEBrowser follow
the specification. Mozilla/Firefox, Opera 9.5+, Konqueror, Safari 1.2+ 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 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.targetwould be a reference to the span, even if the span itself did not have any
listeners attached directly to it. Note that Safari and Konqueror sometimes make the target point to
the textNode inside the element. That is incorrect behaviour, and should be fixed at some point. 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

• Tkhtml Hv3 does this correctly, but because versions before September 2007 activate the listeners in the
wrong order, they appear to fail here.

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 thestopPropagation 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, and Safari. 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 (and
Opera, but not Konqueror or 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 DOM 2 events specification, the load event should be
detected on the documentobject. 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 thewindow object.

So far, Opera, and new versions of Konqueror, Safari and NetFront correctly detect it on the
document object (as well as on the window object, for compatibility reasons). In theory
Mozilla/Firefox will fix this at some point, and although it can be detected on the window object in all
current implementations, new implementations may choose to do things right first time, 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 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);

Congratulations; your browser detected 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

• Safari makes the keyCode and charCode properties read-only, so it is not possible to simulate specific
keys when manually firing events.
• Mozilla/Firefox incorrectly generates the default action for mouse and printable character keyboard events,
when they are dispatched on form elements.
• iCab 3, ICEbrowser, NetFront 3.3- and Tkhtml Hv3 cannot manually fire events (ICEbrowser, NetFront 3.3-
and Tkhtml Hv3 provide the methods, but they do not work correctly).

With the old events model, it was easy to fire event handlers manually,
usingreferenceToElement.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 focusmethod 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','sele
ct', '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','DOMChar


acterDataModified', 'DOMNodeInsertedIntoDocument','DOMNodeRemovedFro
mDocument', 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):

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 initKeyEventmethod expects the following
parameters: 'type', bubbles, cancelable, windowObject, ctrlKey,altKey, shiftKey, metaKey, keyC
ode, 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 Internet Explorer on Windows

Internet Explorer on Windows does not provide any of the DOM 2 Events methods, and provides
very few of the properties of the event object. Version 5.0+ does provide a similar system that is
much more limited in its capabilities, but if you need 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. Internet Explorer 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 only supports the basic event types - it
does not support any of the advanced types, such as mutation events.) If you need 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
aseventObject.fromElement in IE. It is available for mouseover and mouseout events.

var relTarget = e.relatedTarget ? e.relatedTarget : e.fromElement;


Preventing the default action

IE 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

IE does not provide the stopPropagation method (probably because IE 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 IE.

if( e.stopPropagation ) { e.stopPropagation(); }

e.cancelBubble = true;

Test it here: click this link to activate the event listeners.

Detecting the load event

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

Congratulations; your browser detected 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. Internet Explorer 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 andcancelable 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 thecreateEvent 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.
Key

• Parent object
o Child object
o Child property
o Child object being accessed through a collection[]
o Event
o Method()

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
• document
o createDocumentFragment()
o createElement('element_name')
o createTextNode('text content')

To walk the DOM tree


• document
o body
o documentElement
o getElementById('element id')
o getElementsByTagName('element_name')

DOM support
• document
o implementation
 hasFeature(string: feature,string: domVersion)
All nodes

To walk the DOM tree


• IE incorrectly provides element methods like getElementsByTagName on comment nodes.
• node
o childNodes[]
o parentNode
o firstChild
o getElementsByTagName('element_name')
o lastChild
o nextSibling
o offsetParent
o previousSibling

Attaching, copying or removing nodes


• node
o appendChild(nodeReference)
o cloneNode(bool: copyChildrenToo)
o innerHTML
o insertBefore(nodeReferenceX,nodeReferenceY)
o removeChild(nodeReference)
o replaceChild(nodeReferenceX,nodeReferenceY)
o splitText(index)

Node information
• IE incorrectly provides element properties like tagName on comment nodes.
• node
o data
o hasChildNodes()
o id
o nodeName
o nodeType
o nodeValue
o specified
o tagName
o title
Element node attributes
• node
o attributes[]
o getAttribute('attributeName')
o removeAttribute('attributeName')
o setAttribute('attribute_name','attribute_value')

Element node style


• node
o className
o currentStyle
 styleName
o style
 styleName
• window
o getComputedStyle(nodeReference,string: pseudoElement)
 styleName

Element node size and position (not standardised)


• node
o clientHeight
o clientWidth
o offsetHeight
o offsetLeft
o offsetParent
o offsetTop
o offsetWidth
o scrollHeight
o scrollLeft
o scrollTop
o scrollWidth
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.

To walk the DOM tree


• node
o caption
o cells[]
o rows[]
o tBodies[]
o tfoot
o thead

Attaching, copying or removing nodes


• node
o deleteCell(cellIndex)
o deleteRow(rowIndex)
o deleteTFoot()
o deleteTHead()

Node information
• node
o cellIndex
o rowIndex

Stylesheets (if supported)

• document
o styleSheets[]
 addRule(string selectors,string styles)
 cssRules[]
 cssRules[]
 cssText
 deleteRule(int index)
 encoding
 href
 insertRule(string rule,int index)
 media
 parentRule
 parentStyleSheet
 selectorText
 style
 cssText
 item(int index)
 length
 getPropertyValue(string style-name)
 setProperty(string style-name,string styleValue,string
priority)
 getPropertyPriority(string style-name)
 removeProperty(string style-name)
 nameOfStyle
 styleSheet
 type
 deleteRule(int index)
 disabled
 href
 imports[]
 insertRule(string rule,int index)
 media
 media
 appendMedium(string mediaType)
 deleteMedium(string mediaType)
 item(int index)
 length
 mediaText
 ownerNode
 sheet
 ownerRule
 owningElement
 styleSheet
 removeRule(int index)
 rules[]
 parentStyleSheet
 selectorText
 style
 cssText
 nameOfStyle
 title
 type

Events

Adding and removing event listeners


• node
o addEventListener(string: event,function,bool: phase)
o attachEvent(string: onevent,function)
o detachEvent(string: onevent,function)
o removeEventListener(string: event,function,bool: phase)

Creating event objects


• document
o createEvent(string: EventModule)
o createEventObject(optional templateObject)

Preparing event objects


• eventObject
o initEvent('type', bubbles, cancelable)
o initKeyEvent('type', bubbles, cancelable, window, ctrlKey, altKey, shiftKey,
metaKey, keyCode, charCode)
o initMouseEvent('type', bubbles, cancelable, window, detail, screenX, screenY,
clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)
o initMutationEvent('type', bubbles, cancelable, relatedNode, prevValue,
newValue, attrName, attrChange)
o initUIEvent('type', bubbles, cancelable, window, detail)
Firing events
• node
o dispatchEvent(eventObject)
o fireEvent('ontype',eventObject)

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.

• eventObject
o attrChange
o attrName
o bubbles
o cancelable
o cancelBubble
o charCode
o currentTarget
o detail
o eventPhase
o fromElement
o metaKey
o newValue
o preventDefault()
o prevValue
o relatedNode
o relatedTarget
o returnValue
o stopPropagation()
o toElement

You might also like