Table of contents
- Important guidelines
- Introduction to JavaScript
- Examples
- Adding JavaScript to a page
- Object oriented programming
- Operators
- Variables
- Control structures
- Writing with script
- Writing functions
- Referencing
- Browser inspecific referencing
- Browser specific referencing
- DHTML
- Element contents
- Window size and scrolling
- Event information
- Creating objects
- Creating time delays
- Using cookies
- Security
- Semicolons
- The JavaScript object
- W3C DOM introduction
- DOM nodes and tree
- DOM tables
- DOM CSS
- DOM Style Sheets
- DOM events
- DOM objects and methods
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 exceptions are cookies or various client side storage APIs, which can be used by many pages to store and pass information between them, even after the pages have been closed.
Before we go any further, let me say; JavaScript has nothing to do with Java. If we are honest, JavaScript, originally nicknamed LiveWire and then LiveScript when it was created by Netscape, should in fact be called ECMAscript as it was renamed when Netscape passed it to the ECMA for standardisation.
JavaScript is a client side, interpreted, object oriented, high level scripting language, while Java is a client side, compiled, object oriented high level language. Now after that mouthful, here's what it means.
- Client side
- Programs are passed to the computer that the browser is on, and that computer runs them. The alternative is server side, where the program is run on the server and only the results are passed to the computer that the browser is on. Examples of this would be PHP, Perl, ASP, JSP etc.
- Interpreted
- The program is passed as source code with all the programming language visible. It is then converted into machine code as it is being used. Compiled languages are converted into machine code first then passed around, so you never get to see the original programming language. Java is actually dual half compiled, meaning it is half compiled (to 'byte code') before it is passed, then executed in a virtual machine which converts it to fully compiled code just before use, in order to execute it on the computer's processor. Interpreted languages are generally less fussy about syntax and if you have made mistakes in a part they never use, the mistake usually will not cause you any problems.
- Scripting
- This is a little harder to define. Scripting languages are often used for performing repetitive tasks. Although they may be complete programming languages, they do not usually go into the depths of complex programs, such as thread and memory management. They may use another program to do the work and simply tell it what to do. They often do not create their own user interfaces, and instead will rely on the other programs to create an interface for them. This is quite accurate for JavaScript. We do not have to tell the browser exactly what to put on the screen for every pixel (though there is a relatively new API known as canvas that makes this possible if needed), we just tell it that we want it to change the document, and it does it. The browser will also take care of the memory management and thread management, leaving JavaScript free to get on with the things it wants to do.
- High level
- Written in words that are as close to english as possible. The contrast would be with assembly code, where each command can be directly translated into machine code.
- Object oriented
- See the section on 'object oriented programming' for details.
How is JavaScript constructed
The basic part of a script is a variable, literal or object. A variable is a word that represents a piece of text, a number, a boolean true or false value or an object. A literal is the actual number or piece of text or boolean value that the variable represents. An object is a collection of variables held together by a parent variable, or a document component.
The next most important part of a script is an operator. Operators assign literal values to variables or say what type of tests to perform.
The next most important part of a script is a control structure. Control structures say what scripts should be run if a test is satisfied.
Functions collect control structures, actions and assignments together and can be told to run those pieces of script as and when necessary.
The most obvious parts of a script are the actions it performs. Some of these are done with operators but most are done using methods. Methods are a special kind of function and may do things like submitting forms, writing pages or displaying messages.
Events can be used to detect actions, usually created by the user, such as moving or clicking the mouse, pressing a key or resetting a form. When triggered, events can be used to run functions.
Lastly and not quite so obvious is referencing. This is about working out what to write to access the contents of objects or even the objects themselves.
As an example, think of the following situation. A person clicks a submit button on a form. When they click the button, we want to check if they have filled out their name in a text box and if they have, we want to submit the form. So, we tell the form to detect the submit event. When the event is triggered, we tell it to run the function that holds together the tests and actions. The function contains a control structure that uses a comparison operator to test the text box to see that it is not empty. Of course we have to work out how to reference the text box first. The text box is an object. One of the variables it holds is the text that is written in the text box. The text written in it is a literal. If the text box is not empty, a method is used that submits the form.
Examples
All examples on this page and elsewhere in my JavaScript tutorial are provided free of charge. Please see the terms and conditions page for terms of use.
These examples are all useful in their own right but when used together can produce spectacular results. I will leave that to your own creativity.
Note that the syntax highlighted source of all the header files I use in these examples, as well as several others, is available on my JavaScript libraries page. The header files can also be downloaded from there.
The header file scripts are meant to be real scripts for real use, not tutorials. I have included them on this page as they can be viewed as examples of real programming, not lessons on how to program. I agree that they are not always easy to read, but that is mainly because they are optimised for real use. You may choose to see this as a bad programming method, but I do not, as I can always see what I was intending for the script to do, and it helps to cut down on the transmitted file size, something which modem users are always happy for.
To give you an example of a more 'friendly' format, which is easier to use as a learning aid, one of my readers has kindly produced a reformatted version of my generic re-writable elements script, which is included in the email they sent me.
Non DHTML
The most popular use of JavaScript; changing images when the mouse hangs over a link
The next most popular use of JavaScript; opening new browser windows
The next most popular use of JavaScript; writing dynamic content
The next most popular use of JavaScript; checking forms
Slightly less used but just as useful, retaining variables from one page for use on another page
- 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
Rewritable document content
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 demanding on resources. The simpler games may even work in archaic browsers like Netscape 3.
These games are all on my games site.
Adding JavaScript to a page
You can add a script anywhere inside the head or body sections of your document. However, to keep your document well structured there are some basic guidelines:
- 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 of application/xhtml+xml - the majority of pages that use XHTML markup are actually served as text/html and as a result are correctly treated as HTML, not XHTML.
The rules in XHTML are different. Script tags are not treated as being special. Their contents are treated as any other part of XHTML, so various operators can be misinterpreted as part of the markup. To avoid this, it is best to put all scripts in external script files so that they do not interfere with the page itself. If you feel the need to put something directly on the page, you can declare it as CDATA (the default for script contents in normal HTML):
<script type="text/javascript">
<![CDATA[
//JavaScript goes here
]]>
</script>
If you feel the compulsion to comment out your script in XHTML, you must use a more ugly structure to contain your scripts. Again, this really is not needed, since browsers that do not understand script also do not understand XHTML, but in case you want to use it (maybe you are serving it as XHTML to some browsers, and HTML to others), this is what to use:
<script type="text/javascript">
<!--//--><![CDATA[//><!--
//JavaScript goes here
//--><!]]>
</script>
Controlling when the script is activated
By default, any script you write will be processed as the document loads. For example:
<script type="text/javascript">
var myVariable = 'Hello';
window.alert(myVariable);
//as the document loads, a message box saying 'Hello' will be displayed
</script>
Sometimes, this may be undesirable and it may be better to write functions (see the section on Writing functions) and activate them later.
To activate JavaScript, you can use HTML events. Modern browsers can detect a vast number of events on most elements. Older browsers are more limited and can only detect the standard set of events on specific elements. The ones that will work in all browsers I have tested are:
- input, textarea, select
- onclick, onkeydown, onkeyup, onkeypress, onchange, onfocus, onblur, etc.
- form
- onsubmit, onreset
- a
- onclick, onmouseover, onmouseout, onfocus, onblur
- body
- onload, onunload
Check with the HTML specifications on the W3C site for more details. 'a' elements can also detect onmousemove in all current browsers, but not some of the older browsers that are not used any more. For some of these older browsers, such as Netscape 4, it is possible to compensate using a combination of onmouseover, onmouseout and then document.captureEvents and onmousemove on the body (see the section on Event information). The Event information and more advanced DOM events chapters of this tutorial show how to listen for more event types, and obtain information from them.
These are some examples of using events (shown here using traditional inline event handlers - later on in this tutorial, I will show you alternative ways to attach events to elements that will not require you to alter your HTML):
<a onmouseover="name_of_function(params)" href="somepage.html">
<input onkeypress="myVariable = 2;startTest();">
<select onchange="self.location.href = this.options[this.selectedIndex].value;">
<body onunload="window.alert('bye')">
Another way of making HTML activate JavaScript is to use
<a href="javascript:name_of_function(params)">
but in general it is best to use events,
so the link can provide an accessible alternative if JavaScript is not available (the event handler can prevent the
browser from using the link normally if scripting is available).
In all these cases, the script is contained within HTML attributes, and is subject to the normal rules of HTML entities. If you need to put quotes in the script in between the quotes in the html, use the HTML entity " or " for " style quotes or if your quotes in the HTML are done using the ' quotes (which is unusual ...), you can use ' to represent these in the script. If you need to include < or > operators, you should use the < and > entities.
Object oriented programming
Objects
An object is a 'thing'. For example a number is an object. An array is an object. Your browser window is an object. A form in your document is an object. There are hundreds more, and in some cases you can create your own.
Objects can have objects in them. For example, your document can have a form in it. This form is a
'child object' of the document. Therefore the document is the 'parent object' of the form. To reference
the child object, we have to go through the parent object, eg. document.myForm
An array can have numbers in its cells. As we have already discussed, the array is an object, and so
would be a cell within it, and so is the content of the cell. We cannot refer to the cell itself, but
we can refer to its contents: myArray['cell name or number']
for example.
Classes (or types)
A class is a group of objects that are similar in some way. For example, a number and a piece of text can both be stored as a variable (in a way, like the variables you would use in mathematical algebra). In essence, we can say that pieces of text and numbers are examples of class 'variable'.
Numbers can be sub divided into two groups, integers and floats (or doubles). Integers are whole numbers: 1, 2, 3, 4, 0, -1, -2, etc. Floats have decimal points: 1.1, -5.7, 0.5, etc. In this case, we can say that 3 is an instance of class variable, (sub)class number, (sub)class integer.
In fact, a variable is a type of object. All instances of class 'object' have a certain two methods: toString() and valueOf(). Therefore, as 3 is an instance of class object, (sub)class variable, (sub)class number, (sub)class integer, it will inherit the toString() and valueOf() methods provided by the class 'object'.
Classes are not so important in JavaScript as they are in many other object oriented programming languages. Classes can be created when you define your own classes of objects, but it is not usual to create your own 'sub'-classes.
Collections
If you need to know what arrays are, see the section on Variables.
There are many arrays that are inbuilt into each document. The document itself is an array in certain uses.
The most obvious of these collections is the images collection. To refer to images in the document, we use
document.images['name of image']
This is a special kind of array, known as a collection.
Properties
Take, for example, an image. When we define images in HTML, we write:
<img src="frog.gif" name="myImage" height="10" width="10" alt="A frog">
The properties of the image would be src, name, height, width, alt and if we also used Style Sheets we might
have included several more (like background-color for example). All properties are a type of object so to
refer to the src of my image, I would write document.images['myImage'].src
Methods
There are always actions that are associated with objects. For example, a form may be submitted or reset. The
actions are methods. To submit a form in non-object-oriented programs, we might write
submit('name of form')
That would simply be a function. In object oriented programming like JavaScript, we would use
document.nameOfForm.submit()
The reason this is a method and not just a function is that each form will have its own submit() function built in, which is referred to using the object oriented syntax shown above. You will never have to write methods in yourself unless you create your own objects, the browser does that for you.
You can think of it like this:
- 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. The JavaScript 1.3 operators such as the identity
operator ===
are supported by all current browsers, but are not used as often as the others.
Old browsers that do not understand them will just produce errors.
Operator | Uses |
---|---|
+ | adds two numbers or appends two strings - if more than one type of variable is appended, including a string appended to a number or vice-versa, the result will be a string |
- | subtracts the second number from the first |
/ | divides the first number by the second |
* | multiplies two numbers |
% | divide the first number by the second and return the remainder |
= | assigns the value on the right to the object on the left |
+= | the object on the left = the object on the left + the value on the right - this also works when appending strings |
-= | the object on the left = the object on the left - the value on the right |
> | number on the left must be greater than the number on the right - this also works with strings and values |
< | number on the left must be less than the number on the right - this also works with strings and values |
>= | number on the left must be greater than or equal to the number on the right - this also works with strings and values |
<= | number on the left must be less than or equal to the number on the right - this also works with strings and values |
++ | increment the number |
-- | decrement the number |
== | the numbers or objects or values must be equal |
!= | the numbers or objects or values must not be equal |
<< | bitwise leftshift |
>> | bitwise rightshift |
& | bitwise AND |
| | bitwise OR |
^ | bitwise XOR |
~ | bitwise NOT |
! | logical NOT (the statement must not be true) |
&& | logical AND (both statements must be true) |
|| | logical OR (either statement must be true) |
in | object or array on the right must have the property or cell on the left |
=== | the numbers or objects or values must be equal, and must be the same variable type |
!== | the numbers or objects or values must not be equal, or must not be the same variable type |
Note, if you do not set the language="javascript1.2"
attribute in the <script>
tag, 0 == false == ''
and undefined == null
. If you do set the language attribute
to 'javascript1.2', Mozilla/Firefox and other Gecko browsers (but none of the other major browsers)
will change this so that none of these will equate to each other. Since the attribute is deprecated anyway, and
the JavaScript versions were never standardised, you should not rely on this behaviour. If you need that behaviour,
use the ===
and !==
operators.
There are also a few operators that can also be used like functions:
- void
void statement
orvoid(statement)
(see the section on writing functions)- typeof
typeof variable
ortypeof(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.
Operator | What it does |
---|---|
Math.abs(n) | Returns the absolute value of n |
Math.acos(n) | Returns (in radians) cos-1 of n |
Math.asin(n) | Returns (in radians) sin-1 of n |
Math.atan(n) | Returns (in radians) tan-1 of n |
Math.atan2(n,k) | Returns the angle (rads) from cartesian coordinates 0,0 to n,k |
Math.ceil(n) | Returns n rounded up to the nearest whole number |
Math.cos(n) | Returns cos n (where n is in radians) |
Math.exp(n) | Returns en |
Math.floor(n) | Returns n rounded down to the nearest whole number |
Math.log(n) | Returns ln(n) Note, to find log10(n), use Math.log(n) / Math.log(10) |
Math.max(a,b,c,...) | Returns the largest number |
Math.min(a,b,c,...) | Returns the smallest number |
Math.pow(n,k) | Returns nk |
Math.random() | Returns a random number between 0 and 1 |
Math.round(n) | Returns n rounded up or down to the nearest whole number |
Math.sin(n) | Returns sin n (where n is in radians) |
Math.sqrt(n) | Returns the square root of n |
Math.tan(n) | Returns tan n (where n is in radians) |
Variables
Variable types are not important in JavaScript. They may be interchanged as necessary. This means that if a variable is a string one minute, it can be an integer the next. The basic variable types are:
- character
- 'a' or '@' or 'k' etc.
- string
- 'hdttgs 8 -46hs () Get the idea'
- integer
- 0 or 1 or 2 or 3 or 4 etc. or -1 or -2 or -3 or -4 etc.
- float (or double)
- 23.546 or -10.46
- boolean
- true or false
- function
- A function; see the section on Functions
- object
- An object; see the section on Object oriented programming
- array
- A type of object that can store variables in cells (see below)
- regular expression
- A pattern that can be used to match strings or substrings
- undefined
- A variable that has not been given a value yet
- null
- A variable that has been defined but has been assigned the value of null
Integer and float are grouped together as 'number'. Character and string are grouped together as 'string'.
Use the typeof operator to find out which variable type your variable is. Note, typeof will return 'object' for arrays, regular expressions and null in most current browsers. In future, browsers are instead expected to return 'null' for null.
Defining variables
See the section on 'Referencing' subsection 'Avoiding referencing conflicts' to see how to choose names for your variables.
Normal variables
It is good practice to pre-define all variables using the var keyword (this is known as declaring the variables). It is possible to define the variable, leaving its value undefined, or assigning a value immediately, or even to define multiple variables in a single command:
var variablename;
var variablename = value;
var vari1 = value, vari2 = anotherValue, vari3;
JavaScript is fairly lenient, and will create a global variable if you forget to use the 'var' keyword, but you
assign a value to an undeclared variable (if you attempt to read the value of an undeclared variable, it will throw an
error, as discussed below). However,
this can lead to problems if you end up accidentally overwriting variables from another scope (this will be covered in
the chapter on Functions). Once a variable is defined do not use
'var variablename'
again unless you wish to completely overwrite the variable. It makes no sense to redeclare
a variable with the var keyword within the same scope, but browsers will not complain if you do it.
These are some examples of how to define variables, in this case, a string, a number, and a regular expression:
var myFish = 'A fish swam in the river';
var myNumberOfGoats = 23;
var myPattern = /<[^>]*@[^<]*>/gi;
Note that variables can be defined at any point in your script, including inside a control structure that causes that statement never to be executed. However, no matter where you choose to define your variable, the JavaScript engine will create the variable at the start of the current scope. It will hold the value undefined until a statement is executed that gives it a value.
Objects
- 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 single number as a parameter when creating the array object. If the number is a positive integer,
an array will be created with that many cells, all of which will be empty: new Array(5)
. Note that
if you set the language version to javascript1.2, some browsers (but not all) will create an array with a single cell containing
the number 5. For this reason, it is best not to use a single numeric parameter when creating the array using the
new Array(number)
syntax.
To refer to the contents of its cells, use the syntax nameOfArray[name_or_number_of_entry]
(if using names instead of numbers, the name should be in quotes). Numeric arrays start at 0, not 1, so the first
cell is number 0.
In JavaScript, arrays and objects are almost equivalent, and accessing a named cell of an array is the same as accessing a property of an object. However, it is important to note that most of the special array functionality is based on you using numeric arrays - most array methods act only on numeric keys, and will ignore named properties. If you are using arrays with only named properties, then you are almost certainly using them for the wrong reason, and should probably be using objects instead. However, for the purpose of demonstration, if a cell of an array is called 'mycell', you could use either of these to access its contents:
nameOfArray['mycell']
nameOfArray.mycell
There are some important differences between arrays and objects. By default, arrays have far more methods
attached to them, for stepping through a numeric array, or splitting it into pieces. In addition, if numbers are used for
entries, you can find out how many entries (or variables) the array is holding, using nameOfArray.length
It is possible to create multi-dimensional arrays, by creating aditional arrays as the contents of the cell of another array:
var nameOfArray = new Array(new Array(1,2,3,4),'hello',['a','b','c']);
You can refer to the cells of that array using the square bracket notation twice (or multiple times, depending on how many dimensions your array has):
nameOfArray[name_or_number_of_entry][name_or_number_of_inner_entry]
Other types of objects
Browsers will have many built in types of objects. All JavaScript capable browsers will provide the following aditional object types:
- Date
- Creates a date object, which can perform calculations based on the date, for example:
var mydate = new Date(); window.alert( 'The year is' + mydate.getFullYear() );
- Image
- Creates an image that is not visible but is stored in cache. Setting the src attribute of the image causes
the image to be loaded and stored in the browser's cache:
Now each time I want to change an image on the page, I can sayvar myimage = new Image(); myimage.src = 'thing.gif';
document['imagename'].src = myimage.src;
and the image will change, without having to wait for the new image to load. - Option
- Creates an option that can be added into a select input, for example:
var myoption = new Option('text','value'); selectInput.options[selectInput.options.length] = myoption;
See the section on 'Creating objects' for how to create your own objects that are not one of the pre-defined object types. DOM compliant browsers will provide far more object types - these will be covered in later chapters of this tutorial.
Deleting properties
On several occasions here I have created properties for objects. Even if these are set to undefined or null, they still exist, holding that new value, and will be found using the for-in control structure. You can delete them completely by using the 'delete' keyword:
delete myObject.itsProperty;
Of course, unless you have serious computer memory considerations, or security risks, it is usually not necessary to do this at all, since JavaScript engines clean up after themselves once the variable is no longer referenced.
Avoiding errors with variables
If at any time you refer to a variable that does not exist, you will cause an error. However, JavaScript allows you to refer to one level of undefined child properties of an object (specifically, it allows you to refer to non-existant properties of any variable that is not null or undefined, but it will not allow you to refer to their properties, since an undefined property cannot have properties of its own). This is a very important rule to learn, as it forms the basis of object and capability detection, and is fundamental to making cross browser scripts. The following series of examples will demonstrate what will throw errors:
var myObject = new Object(), nonObject = '', nonObject2;
This sets everything up for the following examples. So far, everything is fine, but in the code below, b is undefined. Not only that, but it has never been declared as existing. This will cause an error when the code attempts to read its value:
var a = b + 5;
In the code below, c has also not been defined or declared. It will not throw an error, however, because the code is assigning a value to it. This will cause the script engine to automatically declare it in the global scope:
c = 5;
In the next example, the parent object has been defined, but its property has not. Since this is only one level of undefined properties, this is allowed, and will not cause an error:
var b = myObject.myChild;
In the next example, the parent object has been defined, but its property has not. Trying to refer to a child of the non existent property means there are now two levels of undefined properties. This will cause an error:
b = myObject.myChild.itsChild;
The actual reason it causes an error is because the code attempts to access the property of an object that does not accept properties (implicitly 'undefined' in this case), and will cause an error if an attempt is made to access them. The same would happen if myObject.myChild was explicitly defined as null or undefined.
In the next example, the parent object has been defined, but it is a type of variable that cannot accept custom child properties, such as a number or string. Trying to create a child property will not throw an error, since it is only one level of undefined properties. However, it will not do anything, since the new property will be rejected:
nonObject.itsChild = 7;
window.alert(nonObject.itsChild);
//This will alert 'undefined'
Null and undefined are special, however, as they cannot have any properties at all, custom or native. Any attempt to access a property of a variable that holds the value null or undefined, will result in an error:
nullVariable = null;
nullVariable.itsChild = 7;
//This will throw an error
In the next example, the parent object has been declared, but it was not given a value. As a result, it holds the value undefined. Trying to refer to a child property means there are now two levels of undefined properties, and will throw an error:
nonObject2.itsChild = 7;
Referencing a variable or just its value
If you assign one variable to another, there are cases where this merely copies the value in one variable and 'pastes' it into the other, but there are also cases where instead of copying the variable, it just provides you with a new way to reference that variable. The following is an example of where this might occur:
var myNewVariable = myOldVariable;
If myOldVariable was already defined as a string, number, boolean, null or undefined, it would
simply have copied myOldVariable and 'pasted' it into myNewVariable. If the new variable were
changed (for example, using myNewVariable = 'some new value';
), myOldVariable retains
its old value.
If, on the other hand, myOldVariable was already defined as a function, array, object, regular expression or
option (or any other type of object except null), myNewVariable would have been created as a pointer to myOldVariable. The children of
myOldVariable would now also be the children of myNewVariable. If the new variable were
changed (for example, using myNewVariable = 'some new value';
), it would only alter
the value of myNewVariable so that it no longer references the same contents as myOldVariable.
However, changing the child properties of myNewVariable will change the properties of the
object it references, and as a result, myOldVariable would also see the change:
var myOldVariable = new Object();
var myNewVariable = myOldVariable;
myNewVariable.newChild = 'Hello';
alert(myOldVariable.newChild);
//This will alert 'Hello'
More about numbers
JavaScript understands numbers in several formats, allowing you to specify numbers in hex, decimal, and octal. If a 0 precedes a number and there is no number higher than 7, the number is considered to be in octal (base 8) and if the number is preceded by 0x, then it is considered to be in hex (base 16) and can also contain characters A, B, C, D, E, F. Neither may contain decimal points.
With decimal numbers, 12e+4
may be used to replace 12x104 and 12e-4
may be used
to replace 12x10-4 etc.
There are a few numbers that are held by the Math object and can be used as variables with the exception that you cannot assign values to them. These are known as constants. There are some constants available that give the document area of the window that is available for writing to. These require specific referencing to obtain and so have been included in the section on referencing items, objects and elements, subsection 'Window size'.
The available Math object constants are:
Math object property | Value (approx) | Mathematical 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 |
Math.SQRT1_2 | 0.707 | (sqrt(2))-1 or sqrt(1/2) |
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 in use among some Mac users). Unix based Operating Systems (including Mac OS X) use \n for linebreaks.
If you are writing a string that contains double quotes, the easiest way to deal with it is to surround it with single quotes. You should also use double quotes if your string contains single quotes:
var mystring = 'And he said "help" twice';
var mystring = "Help Andy's cat";
You may notice a problem. What if you have both single and double quotes in your string? The solution is to use the escape character '\' to excape the quotes (in fact, you can always escape quotes, even if the string is delimited by the other type of quotes). For example, both of the following are valid:
var mystring = 'And he said "help Andy\'s cat" twice';
var mystring = "And he said \"help Andy's cat\" twice";
If your string becomes too long to fit on one line (this really doesn't matter - you can make the text as long as you want), and you want to split it onto several lines, simply end the string, follow it with the concatenation operator + and start a new string on the next line:
var mystring = 'first line of the string ' +
'still on the first line of the string - it\'s like we never broke the line';
Note, in theory, you can use \ to break the string:
var mystring = 'like \
this';
But this has significant problems; the \ will not always cope with trailing whitespace, may or may not correctly treat the leading whitespace before the new line, and does not work correctly in all JavaScript implementations:
- Netscape 4 (inventors of JavaScript), Escape, Opera 7.6- and ICEbrowser include the linebreaks in the string.
- Internet Explorer, Mozilla/FireFox, Opera 8+, Safari/Chrome/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 provide
document.body
. In these browsers, writing 'if( document.body.style )
'
would just produce an error (see the section on 'Variables' subsection
'Avoiding errors with variables').
In order to solve this problem, we could write this:
if( document.body ) {
if( document.body.style ) { etc. }
}
However, the &&
operator has a useful feature that we can use here to combine the two
'if' statements into one:
if( document.body && document.body.style ) { etc. }
The first test would be false, so the browser would not proceed to the second. This is known as a short-circuit.
The ||
operator has a similar feature, but it will only evaluate the second test if the first one fails.
JavaScript understands that if the '{' and '}' (curly brace) characters are left out, then only the next command belongs to that statement:
if( x < 5 )
x++;
window.alert(x);
Here, the alert will always happen reguardless of x, but x will only be incremented if x is less than 5. This may seem convenient, as it allows you to make your code a tiny bit shorter, but I recommend avoiding this syntax. It makes your code harder to read, especially if you start nesting your control structures. It also makes it easy to forget to put them in when you needed them, and also makes debugging code much harder, since you will need to go back through your code to add them so that you can add extra debugging tests. It is best to always use the curly braces, even if they are optional.
As always, there is an exception. Nested 'if' statements like this can start to get difficult to manage:
if( myVariable == 2 ) {
myVariable = 1;
} else {
if( myVariable == 5 ) {
myVariable = 3;
} else {
myVariable = 4;
}
}
By strategically removing curly braces, that can usefully be reduced to this construct (which you may recognise from other programming languages) - note that 'else if' is not written as 'elseif':
if( myVariable == 2 ) {
myVariable = 1;
} else if( myVariable == 5 ) {
myVariable = 3;
} else {
myVariable = 4;
}
The 'for' loop
This is one of the most common constructs in use. Typically, it is used to cycle through the contents of an array, or to create a specific number of new objects, but it can do many more useful things if needed. The syntax of the 'for' loop is as follows:
for( starting_initialise; continue_as_long_as_condition; do_this_each_time )
- starting_initialise
- This is where you define new variables that you will use in the loop, typically for use with incremental counting.
As with all variables, you must declare them (if you have not done so already). You can define multiple variables if
needed, using:
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).var myVariable1 = value, myVariable2 = another_value;
- 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:
If the condition is not satisfied when the for loop begins, then it will never loop through it.myVariable1 <= 5 && myVariable2 >= 70;
- 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 was its only major failing. Thankfully these browsers are hardly used any more. It may have been useful to use this structure detect errors in Netscape 4 (like the 'this' keyword + inline method bug - for example - there are lots more errors), but that browser does not support the statement. It would also be useful for checking for stupid bugs, like where checking for something like navigator.taintEnabled causes errors in older versions of Internet Explorer. However, the error is not correctly thrown for these errors.
Unfortunately, if you use this structure in any script run by a browser that does not support it, the browser will abort the entire script with errors, even if it does not use the part containing the structure. Thankfully, these old browsers can be safely ignored.
It should never be used to detect if a browser supports a method or property like document.getElementById as a proper object detect would suffice.
So when should it be used?
It can be used for W3C DOM scripting, where you may want to avoid DOM mutation errors (for example), which are valid errors, but serve to warn you not to do something, and do not always need to abort the whole script. Older browsers do not support the DOM anyway, so it doesn't matter if they don't understand this part of it. However, they will still run the script (it is not possible to protect them by using the language attribute on the script tag, as you need to use JavaScript 1.2 - not anything higher - to enable Internet Explorer 5 support). This means that the older browsers will still produce errors, unless you define the old error handling method in an earlier script.
It can be used for throwing your own errors if you create the 'error' deliberately under certain circumstances.
It can be used to check if accessing a frameset frame will cause a browser security error (for example, if the page in the frame belongs to another site).
It could also enable you to avoid problems where different browsers support the same methods but expect a different syntax, for example, the selectBox.add method (I did not include this method in my DOM section of the tutorial due to this problem):
try {
selectBox.add(optionObject,otherOptionObject);
} catch ( e ) {
selectBox.add(optionObject,index);
}
Conditionals without a condition?
You may notice in the example for "The quick 'if' statement" that I tested for a property without testing
for any specific value: 'if( document.getElementById )
'
That is valid, and is one of the most useful parts of JavaScript. This is a very important rule to learn, as it forms the basis of object and capability detection, and is fundamental to making cross browser scripts. This will be true if:
document.getElementById != "" &&
document.getElementById != 0 &&
document.getElementById != false &&
document.getElementById != undefined &&
document.getElementById != null
The opposite is also possible: 'if( !document.getElementById )
'
This will be true if:
document.getElementById == "" ||
document.getElementById == 0 ||
document.getElementById == false ||
document.getElementById == undefined ||
document.getElementById == null
Using this, you can detect one type of capability, and if it fails, detect another type, and continue until you find one that works.
You can also do this anywhere where a condition is expected, such as with the 'while' loop condition, the 'do - while' loop condition and the 'continue_as_long_as_condition' in the for loop.
Checking for properties with 'in'
- 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 the typeof operator, as shown here:
if( typeof( document.body.innerHTML ) == 'string' ) {
However, it is also possible to use the 'in' operator to test for a property. This allows you to test for its existence, no matter what value it currently holds, and no matter what type of value it currently has (even if it has been assigned a value of undefined). In the 'for - in' loop, the 'in' operator returned the name of properties as a string, and so here, it expects the name to be a string. This limits the usefulness a little, as it can only search for the name, and cannot be used to see if one of the properties holds a specific value or value type.
if( 'innerHTML' in document.body ) {
Note that this is around 20 times slower in Internet Explorer than the conditional without a condition, as shown above. In most other browsers, the two alternatives perform about the same. In general, I consider it best to use the more common alternatives, unless you have a specific use that needs the behaviour of the 'in' operator.
Assignments inside a conditional
JavaScript allows you to perform an assignment at the same time as testing if the assignment worked. This can be used inside any conditional, including inside an 'if', 'for', 'while' and 'do - while'.
if( x = document.getElementById('mydiv') ) {...}
do {
alert( node.tagName );
} while( node = node.parentNode );
Note that Internet Explorer on Mac will produce an error if you try to do this with an array, when it steps off the end of the array.
Continue and break statements and labels
Labels
Labels are used to name the 'while', 'do - while', 'for', 'for - in' and 'switch' control structures. The syntax used is:
LabelName:
Control Structure
Labels are very rarely used in JavaScript.
The break statement
Writing break inside a switch, for, for-in, while or do - while control structure will cause the program to jump to the end of the statement. If you just use, for example:
for( var x = 1; x < 5; x++ ) {
var y = 1;
while( y < 7 ) {
y++;
if( y == 5 ) { break; }
document.write(y);
}
}
The script will jump past the end of the while loop when y is 5. But if you use this:
myForLoop:
for( var x = 1; x < 5; x++ ) {
var y = 1;
while( y < 7 ) {
y++;
if( y == 5 ) { break myForLoop; }
document.write(y);
}
}
The script will jump past the end of the for loop when y is 5.
The continue statement
Writing continue inside a 'for', 'for - in', 'while' or 'do - while' control structure will cause the program to jump to the test condition of the structure and re-evaluate it having performed any 'do_this_each_time' instructions. If you just use this, for example:
for( var x = 1; x < 5; x++ ) {
var y = 1;
while( y < 7 ) {
y++;
if( y == 5 ) { continue; }
document.write(y);
}
}
This script will jump to the test condition of the while loop when y is 5 so 5 will never be written but 6 and 7 will be. If you use this instead:
myForLoop:
for( var x = 1; x < 5; x++ ) {
var y = 1;
while( y < 7 ) {
y++;
if( y == 5 ) { continue myForLoop; }
document.write(y);
}
}
Here, the script will increment x as part of the for loop and then re-evaluate the for condition.
Writing with script
Be careful when writing with script. If script is not available, that content will not be created.
You should limit your use of this to parts of the page that are not needed to access page content.
If you do write important parts of the page that are unavailable without scripting, you should use
<noscript>
tags to provide an alternative.
Writing while the page is still loading
If your code is being executed while the page is still loading (in other words, if it is run as part of the initial page layout) put the following:
<script type="text/javascript">
document.write('<p>What ever you want to write<\/p>');
document.writeln('<p>What ever you want to write<\/p>');
//writeln puts a line break after the line.
//This is treated as a line break in the source of HTML
</script>
It will write whatever you put, in whatever part of the page the script is currently running. Of course, you can include HTML tags in there too or some pre-programming.
Note that if you write content using an event handler (such as the onload handler for an image), it will be treated as if the page has completed loading, even if it has not.
Writing after the page has loaded
After the page has completed loading, the rules change. Instead of adding content to the page, it will replace the page. To do this, you should firstly open the document stream (most browsers will automatically do this for you if you just start writing). Then you should write what you want, and finally, you should close the document stream. Again, most browsers will automatically close the stream for you. The notable exception here is the Mozilla/Firefox family of browsers, that will continue to show a loading graphic until you close the stream. Some other browsers may fail to render part or all of the content. Just to be safe, make sure you always close the stream.
<script type="text/javascript">
window.onload = function () {
document.open();
document.write('<p>What ever you want to write<\/p>');
document.write('<p>More stuff you want to write<\/p>');
document.close();
};
</script>
That will remove everything that is currently being shown and replace it with what you write in there. This is the equivalent of moving the user to a completely new page. You should put <html> tags and things like that in there too if you want to use that method.
You may notice that I close my HTML tags inside the script with a backslash before the forward slash in the closing tag. This is a requirement of the specification (and can cause the HTML validator not to validate your page if you forget it), although all browsers will understand if you omit the backslash.
However, since you can write HTML with script, you can write style or even script tags with it, making one script import another. If you omit the backslash on any </script> tags that you are writing with the script, the browser will read that as the closing tag for the current script, and your script will fail.
The same applies to opening or closing comments (although I fail to see why you would want to write comments using a script).
These can be written as '<'+'!--'
and '-'+'->'
. When the script runs, the plus
sign tells it to append the strings, creating a valid HTML comment.
Problems with old browsers
Although not in use any more, you may want to be nice to older browsers like Netscape 4. If using document.write to dynamically write a div element, do not give the div an inline style with left: or top: positioning. This will cause Netscape 4 to fail completely and be unrecoverable without careful use of dialogs and task kills.
Also, Netscape 4 will completely fail to load if you attempt to use this method to create a
<div style="position:absolute">
and instead, you should use the unnofficial
<layer>
tag. You can use if( document.layers )
to
detect if the layer tag should be used.
Writing functions
General syntax
- 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 use arguments.callee as shown below).
- The Function class constructor
functionName = new Function("function code should be written here");
This construct evaluates the code as a string, and is much slower than assigning anonymous functions. It should only be used in places where it is actually needed.
- The Function class constructor with parameters
functionName = new Function("varName","varName2","etc.","function code");
See the section on 'Referencing' subsection 'Avoiding referencing conflicts' to see how to choose names for your functions.
Functions are called using one of these:
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 the arguments collection (which can also be referenced as referenceToFunction.arguments).
You can use the arguments collection to refer to the arguments, even if you did not write the variable name in the function definition, or you can mix it so that some variable names are defined but others are only available using the arguments collection:
function functionName(variable1,variable2) {
window.alert(variable1); //alerts 5
window.alert(arguments[0]); //alerts 5
window.alert(variable2); //alerts 6
window.alert(arguments[1]); //alerts 6
window.alert(arguments[2]); //alerts 7
window.alert(functionName.arguments[3]); //alerts 8
}
functionName(5,6,7,8);
The arguments collection also has a very useful property; arguments.callee. This is a reference to the function itself, meaning that code running inside an anonymous function can still obtain a reference to the function that is being run. This property is not available in some older browsers.
Using the return statement
The return statement causes a function to stop executing at that point. The code that called the function will still continue to execute.
function doWhatever() {
var apod = Math.pow(3,7);
return;
//the following code will not be executed,
//no matter what
apod *= 34;
if( 700 * 3 <= apod ) {
return;
//return on its own is more usually
//used as part of a conditional
} else {
window.alert('The script has made a mistake');
}
}
The following is an example of using the return statement to return a variable from a function:
function appendComment(passvar) {
//in this case, I have passed a string variable and I return
//a string variable. I could return any variable type I want.
passvar += ' without your help';
return passvar;
}
var myString = appendComment('I did it');
//myString is now 'I did it without your help'
Note that if you need your code to work in older browsers, it is important to make sure that if the return statement is used to return a value, you must ensure that in all cases, the function returns a value, or some Netscape 4 versions will produce errors.
Take, for example, the action of fading. I want to write the same thing repeatedly, slowly fading from one colour to another. Rather than having to calculate this manually, I want to run some script that calculates the fade for me. The script will run nicely as part of a function, where the function returns the next colour at each step, which I can then use. This is useful, because I can then use the same function to produce a variation of the same effect later on, based on different parameters.
function fadeColour( fromcol, tocol, fadePortion ) {
//in the format fadeColour( 'ff0098', 'fe0934', 0.23 )
var oF = [], oT = [], oP = [];
var oH = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
//get the red, green and blue substrings ...
for( var x = 0; x < 3; x++ ) {
//... and convert them from hex into decimal ...
oF[x] = eval( '0x' + fromcol.substring( 2 * x, ( 2 * x ) + 2 ) );
oT[x] = eval( '0x' + tocol.substring( 2 * x, ( 2 * x ) + 2 ) );
//... add on the required portion of difference between the two ...
oP[x] = Math.round( oF[x] + ( ( oT[x] - oF[x] ) * fadePortion ) );
//... and convert it back into hex ...
oP[x] = oH[ ( oP[x] - ( oP[x] % 16 ) ) / 16 ] + oH[ oP[x] % 16 ];
}
//... and put them back together again as a colour string
return '#' + oP.join('');
}
for( var y = 0; y < 10; y++ ) {
//in 10 steps, fade the colour - also see the section on writing with script
document.write( '<span style="color:' + fadeColour( 'd2cbff', '000099', y / 9 ) +
';">Fade!<\/span> ' );
}
for( var y = 0; y < 12; y++ ) {
//in 12 steps, fade the colour
document.write( '<span style="color:' + fadeColour( 'ff0000', '000000', y / 11 ) +
';">Fade!<\/span> ' );
}
Warning, if you are returning a value from a function, do not call the function directly from
the <a href=
method to activate it, or many browsers will (correctly) create a new page containing
the returned value as the page content. You can still use return;
on its own. You can only return
values if you call the function script from another piece of script (like another function).
If you need to call the function using the <a href=
method, use the void operator.
Note, returning false for many events will cancel the action. As an example, if a user submits a form, returning false will cause the form not to be submitted. The exception is onmouseover, where returning true will stop the mouseover from having any effect (such as with a link, where returning true will stop the link url from being displayed in the status bar).
Variable scope
Variable scope is one of the very useful features of languages like JavaScript, so even though it may seem a little complicated, it is worth taking the time to learn how it works.
In a basic script, there may be any number of variables and functions. Take the following example:
var a = 1, b = 2, c = 3;
function sample() {
var d;
a = 7;
}
sample();
alert(a);
The variables a, b and c are not inside any function when they are declared, so they are in a scope called the global scope. They are available anywhere. Code anywhere else in the script, including inside the sample function, has access to them. The sample function is also global, since it is not inside any other functions. If any other piece of code changes the contents of those variables, then every other part of the code now sees the new contents. As a result, the alert here will show '7', since the value held by the global variable is changed when the function is run.
Variable d is defined inside the sample function, so it is not global. It is in the local scope of the function. What that means is that only the code inside the function can see it. Code outside the function does not even know it exists. This happens with any function. They have the ability to create their own scopes, and their own local variables, without them interfering with variables located in the global scope. Variable names written in the brackets of the function definition are also created as variables in the local scope (and the same applies to the arguments collection):
function sample(myvar) {
//myvar is now a variable in the local scope
alert(myvar);
}
sample('hello');
Now try this modification to the earlier code:
var a = 1, b = 2, c = 3;
function sample() {
var a, d;
a = 7;
}
sample();
alert(a);
Here, the variable a is redefined inside the function. Because it is declared using the var keyword, it becomes a local instance of a variable. Even though it shares the same name as a global variable, the two are completely independent. The changes made to that variable inside the function only affect the local variable, not the global one. As a result, the alert will show '1', and not '7'.
Now imagine that the code inside the function wants to reference the global variable 'a' instead of the local one.
The global scope is special. In JavaScript, the global scope can be referenced using the name 'window'. The code
inside the function can use window.a
to reference the global 'a' variable. Incidentally, this is why
methods like alert can be called using either alert or window.alert, since these methods are
globally available (unless they are overwritten in the current scope, of course).
Nested functions
It is possible to create functions inside other functions. Simply declare another function inside the code of an existing function:
var a = 1, b = 2, c = 3;
function sample() {
var a, d, e;
function anothersample() {
var e, f;
}
anothersample();
}
sample();
In that example, the anothersample function only exists inside the sample function, and it can only be called by the code inside the sample function. Code outside that function does not even know it exists. The scopes are also nested, so the code inside the anothersample function has access to b and c from the global scope, a and d from the sample scope, and then e and f from its own local scope. It can also use window.a to reference the variable a from the global scope.
Of course, if you assign a reference to the nested function to a global variable, then the function can be accessed globally through that variable. There is not much point in doing that here, but it becomes very useful when creating object methods (these will be covered in detail in a later chapter).
Scopes have memory
Scopes are actually very clever, since they persist over time. Imagine that the code inside a function creates a local variable. It also creates an event handler function that will be triggered when the user clicks a link:
function sample() {
var a = 20;
document.links[0].onclick = function () {
alert(a);
};
}
sample();
The action that calls the event handler (inner) function happens much later, a long time after the script that created it has finished running. However, the variable 'a' has survived, so the alert will display the number 20.
Using scope to prevent conflicts
Imagine that you are running a script that uses many global variable and function names. You want to put another script on the same page, but there is a chance that the two scripts will use the same variable names as each other, and may conflict. It is easy to workaround this problem by putting the code from each script inside a function, and running the function. That way, the local scope provided by the function will protect the variables overwriting each other in the global scope. Note that when doing this, it is very important that you remember to declare your variables properly.
The easy way to do this without creating a global function is to create an anonymous function, and enclosing it in parenthesis (internally, the engine will then replace that entire construct with the function reference). You can then use the open-close parenthesis to run it immediately. This construct may look a little odd, but it works:
(function () {
//Put your script code in here
})();
Using nested function scope to preserve instantaneous values
This is an advanced topic that is covered in its own article.
Referencing
How browsers affect it
Referencing is, in my experience, the hardest thing to learn to get right. It is not helped by the fact that the methods required may be different in different browsers and so you have to include several different versions of the code to ensure it will work in all of them. I have put together a list of the most popular browsers available that support JavaScript.
Opera, Mozilla and Safari/Chrome-based browsers currently have the highest level of JavaScript support, but many scripts are poorly written, and are based on the implementation of just a few browsers, typically just Internet Explorer and one other browser. Unfortunately, Internet Explorer 8- does not have a very good JavaScript implementation. It often does not comply with the standards, relying instead on authors to use its non-standard extensions. Many authors do not realise these are non-standard, and end up writing code that relies on these extensions. Other browsers may implement some of these extensions in an attempt to get these scripts to work. To make matters worse, when running in quirks mode, Internet Explorer 9+ removes support for the standards approach, and reverts to the scripting support of earlier versions. Scripts that need to run in both standards and quirks mode will still need to allow for both approaches, even if older versions of Internet Explorer are not being used.
What I have done in this tutorial is to carefully test the responses of as many different browser engines as I could, and put together something that works in as many of them as possible. Where possible, I will rely on the standards compliant version, and only fall back to the alternatives if a standards compliant technique cannot be used.
Although the W3C DOM makes it possible to access any object using a standard DOM structure, the older approaches are still possible, and widely used on the Web. These older approaches will be covered first in this tutorial, as it is necessary to know them when dealing with code that is designed to work in all possible browsers, including the older ones.
Object hierarchy
To reference items (objects) on the page, refer to an object structure or hierarchy. The topmost object is usually 'document' and the next will be different depending on the document structure. Most elements (HTML tags) cannot be referenced in all 4th generation browsers. Those that can are:
- 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 4th generation 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, older Gecko browsers (Mozilla/Firefox 1/Netscape 6+) will need at least one non-entity character to be written
between the element's opening tag and the script, even if the element is an image, or the element will not be available
to script until after the page loads.
There are a few others elements that can be referenced. For a list of objects, properties, collections and methods, refer to the section on The JavaScript object. In 5th generation browsers (the vast majority of browsers in use today), all elements can be rederenced using the DOM. This will be covered in later sections.
I have written a generic referencing function that can be used to reference all document components that can be referenced (except links), but I suggest you work through the next few sections of the tutorial first instead of taking a shortcut. You might actually understand what I am doing, instead of blindly using my code without knowing how it works.
Avoiding referencing conflicts
Remember that all names in JavaScript are case sensitive. Names must begin with a-z or A-Z or _ and may only contain the characters a-z, A-Z, 0-9 and _. The convention is to use names that describe the object. If the name is made up of more than one word, there should never be a space between the words and it is not usual to have an underscore ( _ ) between the words. The usual practice is to capitalise the first letter of all words except the first.
For example, I have a variable that I will be using to store some text about me. I decide the best name is 'text about me' and I must remove the spaces and capitalise the first letters of all words except the first. Therefore, the variable name becomes 'textAboutMe'.
In order to avoid referencing conflicts, you must make sure that no two JavaScript objects or document elements are given the same name (except inputs - see the next section).
When choosing names for variables, functions and both names and IDs of HTML elements, there is a set of reserved words which you must not use, regardless of the case. These are: abstract, arguments, boolean, break, byte, case, catch, char, class, const, continue, debugger, default, delete, do, double, else, enum, export, extends, false, final, finally, float, for, function, goto, if, implements, import, in, instanceof, int, interface, long, native, new, null, package, private, protected, public, return, short, static, super, switch, synchronized, this, throw, throws, transient, true, try, typeof, var, void, volatile, while, with.
In addition, it is generally a good idea not to use names that conflict with the names of existing properties or methods of the global/window object (unless you really want to overwrite them). See the section on The JavaScript object for more details.
Browser inspecific referencing
Note that to avoid referencing problems, no two elements should ever be given the same name or id. The only exception is form inputs which can share names.
Global references
Variables are referenced simply by typing their name, or by using window.variableName for global variables.
Functions are referenced simply by typing their name, or by using window.functionName for global functions.
'window' (or 'self') may be used to reference the global object for the current document,
regardless of what scope your script is running in. If your script is running in another script and you are
having trouble referencing global variables because a local variable uses the same name, this problem
can be quickly solved by adding 'window.'
to the start of the variable name. For example window.myvariable
By a similar nature, you can use 'this' to represent the current object. By default, 'this' will be the window object. If you are writing the script as part of a HTML event handler, 'this' will be the element that detected the event. For example, this can be used to alert the value of the text input:
function myFunction(x) { window.alert(x.value); }
...
<input type="text" onkeypress="myFunction(this)">
Using this same function from a script running in the global scope, the word 'this' refers to the window object. The window object does not have a value property, so the function would alert 'undefined'.
However, if the onkeypress method is activated manually, the word 'this' once again refers to the input, so the function will alert the value of the input:
document.forms[0].elements[0].onkeypress();
Frameset references
There are four very useful references that refer to parts of a frameset. These are 'window' or 'self', 'window.parent', 'window.top' (window is usually omitted) and the window.frames collection.
- self and window
- These refer to the global object of current web page.
- parent
- Refers to the window object of the page that is holding the current page in a frameset.
- top
- Refers to the window object of the page at the top of the frames hierarchy.
- window.frames[nameOrNumberOfFrame]
- Refers to a frame or iframe held by the current page.
With iframes (if the browser supports them) you have to use the extra word 'window' on the end to reference the window object of the document contained by the iframe (there are alternatives, but this one works in all browsers that support iframes, making it superior to all other techniques, since they only work in a selection of browsers):
window.frames[nameOrNumberOfFrame].window
If the page is the only page being displayed, top, parent, self and window will be equal. If the page is being held within a frameset, self and top will not be equal. If the page is the page containing the frameset, and it itself is not being held within a frameset, self and top will be equal. If someone is loading your page into their frameset and you don't want them to, you can use the self-top relationship to remove your page from their frameset and replace their frameset page with your page using:
if( self != top ) { top.location.replace(self.location.href); }
Note, I could have used this:
if( self != top ) { top.location.href = self.location.href; }
However, that makes the browser add the new entry into its history, so if they clicked their back button, they would be forwarded back to your page again. Unfortunately, Gecko browsers (Mozilla/Firefox/Netscape 6+) will only allow the second option as they have very high security levels relating to cross-site scripts.
Note that using scripts to force pages in and out of framesets is a very bad idea. It causes usability problems for many users, breaks search engines, and annoys your visitors. I know you know how to do it, but that does not mean that you should.
With all frames, the name is set using the name="nameOfFrame"
attribute in the
<frame ...>
tag. The number is automatically generated by the browser in the order that the frames
are defined in the HTML, beginning at 0.
To fully reference the window object of another frame when inside a frameset, use one of these:
- 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. Since these browsers are no longer in use, this behaviour can be safely ignored.
To fully reference a form, use document.formname for a form that was defined with the
name="nameOfForm"
attribute in the <form ...>
tag, or
document.forms[number_of_form] either should work. The number is generated automatically by the browser in
the order that the forms are defined in the HTML, beginning at 0.
To fully reference an input, use any of these:
reference_to_form.inputname
reference_to_form.elements['inputname']
reference_to_form.elements[number_of_input_(not_image_input)]
Name is defined using the name="nameOfInput"
attribute in the <input ...>
,
<textarea ...>
, <button ...>
or <select ...>
tags.
Number is generated automatically by the browser in the order that the inputs are defined in the HTML, beginning at 0.
You can read or write the value of text inputs and textareas with their 'value' property. See the section on The JavaScript object for a full list of what parts of a form can be read or changed.
If more than one input of any kind in the same form share the same name (as is common with radio button inputs), they must be referenced using the numerical array associated with that name. The array will contain an entry for each input with that name, in ascending order of how they appear in the source of the document, beginning at 0. For example, if a radio button has the name 'mybutton', it can be referenced using this:
document.nameOfForm.mybutton
But if two radio buttons share the name 'mybutton', the second button (for example) can be referenced using this:
document.nameOfForm.mybutton[1]
This will be the case for any input type, even if inputs of different types share the same name. In practice, radio buttons will almost always share a name, and checkboxes may occasionally share a name. Even if inputs share a name, they will still have individual entries in the elements collection.
For select boxes, each individual option can be referenced using
input_name.options[number_of_option]
. For example:
if( document.myForm.mySelect.selectedIndex == 1 ) {
document.myForm.mySelect.options[3].selected = true;
if( !document.myForm.myRadio[1].checked ) {
document.myForm.myRadio[2].checked = false;
document.myForm.myRadio[1].checked = true;
document.myForm.myRadio[0].checked = false;
}
}
You can reference the value of an option tag with '.value' and the text it displays with '.text'. You can also find out how many options there are by using the '.length' property of the options collection.
Note, setting a select option to null will remove it from the select box:
document.forms[number_of_form].mySelect.options[0] = null;
All other options will have their index numbers adjusted to reflect this change. To add or replace one, use this:
document.forms[number_of_form].mySelect.options[number] = new Option('text','value');
Most commonly, number used is the length of the collection, as that will add the new option onto the end:
document.forms[number_of_form].mySelect.options.length
Warning: although the selectInputObject.selectedIndex property should be read-write, it is read-only in Internet Explorer 4. If this causes problems, you car use this:
selectInputObject.options[index].selected = true;
Images
Note that this changes in Netscape 4 and other layers browsers if the image is put inside a positioned element. See the section on Element contents for more details. Since these browsers are no longer in use, this behaviour can be safely ignored.
To reference an image, use any of these:
- 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 link is put inside a positioned element. See the section on Element contents for more details. Since these browsers are no longer in use, this behaviour can be safely ignored.
To reference an <a ...>
element, there are two ways (excluding those provided by the DOM).
If the name attribute is set, so making the element an anchor (used in <a href="#nameOfLink">
)
you can refer to it using this:
document.anchors[number_of_<a>_element]
Alternatively, if the href attribute is set, you can refer to it using this:
document.links[number_of_<a>_element]
Number is generated automatically by the browser in the order that the links or anchors are defined in the HTML, beginning at 0.
As <area ...>
tags also have the href attribute, they may also be accessed using the links collection, but not
the anchors collection. If an <a ...>
element has both the the href and name attributes set, it will appear in
both collections, although its index may be may not be the same in each. In layers browsers, any links or anchors referred to using the
anchors collection will have only the name property available. The collection provides access to the anchors purely
for the purpose of changing the name, used when setting the location with location.hash.
Browser specific referencing
The fact that this referencing is browser specific does not mean that it will not work in all browsers. What it does mean is that you will have to include more than one way of referencing items to ensure that you manage to reference them correctly. There are four ways to reference items:
- 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, or
display: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, the elementObject.offsetLeft and elementObject.offsetTop properties are given. These give the position of the link relative to an arbitrary ancestor node, known as the offsetParent. Different DOM browsers give a different offsetParent, some may say the paragraph that the link is in, while some may say the container the paragraph is in. This can all get a bit complicated.
Fortunately, with DOM browsers, the offsetParent will also have the offsetTop and offsetLeft properties, giving its position relative to its offsetParent, and so the cycle continues, until the offsetParent is the topmost node in the document, which will always be at offset (0,0). We can access the offsetParent using elementOrCurrentNode.offsetParent. So all we have to do is add the offsets together and we will end up with the same answer as we would if we had the linkObject.x and linkObject.y properties.
There is one exception to this; if an element has position:fixed
, its offsetParent should be
the viewport, which cannot be referenced, so the offsetParent is null instead. However, its
offsetTop and offsetLeft still hold real values. (The situation is actually the same for the
HTML or BODY element when they are attached to the viewport, but they generally have no offset there,
so it does not matter.) What this means is that even when the offsetParent is null, you still need to remember
to add on the last set of offsetTop and offsetLeft values.
Opera 9+, Internet Explorer, and ICEBrowser get this right. Other browsers will treat the offsetParent of a fixed position element as either the HTML
or BODY elements, but this is harmless because they themselves have no offsets, so the algorithm will always work.
The following function returns an array containing [leftPosition,topPosition] for an element.
function findPosition( oElement ) {
if( typeof( oElement.offsetParent ) != 'undefined' ) {
for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
posX += oElement.offsetLeft;
posY += oElement.offsetTop;
}
return [ posX, posY ];
} else {
return [ oElement.x, oElement.y ];
}
}
Test it here: get the position of this link.
With links, you can also check what the text of the link is:
var theText = linkObject.text ? linkObject.text : linkObject.innerText;
Element position with scrolling offsets
- 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 the offsetLeft and offsetTop.
As a separate complication, there is the document scrolling. The document scrollbar can be produced by either the BODY or HTML elements, depending on the browser, DOCTYPE, and overflow styles on the elements. Different browsers also reflect this scrolling in different ways when checking for scrolling on these elements, and this makes it completely unreliable. The easiest approach is simply to exclude any scrolling on these elements. If needed, it can be added later by working out how far the document has been scrolled (covered in the window size and scrolling chapter).
The scrolling offset for each element is available as the scrollTop and scrollLeft properties for those elements. The following code performs as described (including making sure it does not subtract any scrolling for the target element itself). Make sure all scrollable elements have the position style set as shown above.
function findPositionWithScrolling( oElement ) {
if( typeof( oElement.offsetParent ) != 'undefined' ) {
var originalElement = oElement;
for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
posX += oElement.offsetLeft;
posY += oElement.offsetTop;
if( oElement != originalElement && oElement != document.body && oElement != document.documentElement ) {
posX -= oElement.scrollLeft;
posY -= oElement.scrollTop;
}
}
return [ posX, posY ];
} else {
return [ oElement.x, oElement.y ];
}
}
Note that this performs much more slowly than the simple findPosition script, so it should only be used where absolutely necessary.
It is possible to use DOM to step up the entire parentNode chain and subtract the scrolling offsets for elements from the value returned by the simple findPosition script. If the computed position style is fixed it can stop. If it is absolute it can stop including scrolling offsets until it reaches another element whose computed position style is not static (done by jumping to the element's offsetParent). This would then work without needing to set the position style on all scrollable elements. However, asking a browser to perform this many computations will cause the script to run very slowly, so should be avoided. I do not recommend using this code, but it is here if you still feel you need it:
function findPositionWithScrolling( oElement ) {
function getNextAncestor( oElement ) {
var actualStyle;
if( window.getComputedStyle ) {
actualStyle = getComputedStyle(oElement,null).position;
} else if( oElement.currentStyle ) {
actualStyle = oElement.currentStyle.position;
} else {
//fallback for browsers with low support - only reliable for inline styles
actualStyle = oElement.style.position;
}
if( actualStyle == 'absolute' || actualStyle == 'fixed' ) {
//the offsetParent of a fixed position element is null so it will stop
return oElement.offsetParent;
}
return oElement.parentNode;
}
if( typeof( oElement.offsetParent ) != 'undefined' ) {
var originalElement = oElement;
for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
posX += oElement.offsetLeft;
posY += oElement.offsetTop;
}
if( !originalElement.parentNode || !originalElement.style || typeof( originalElement.scrollTop ) == 'undefined' ) {
//older browsers cannot check element scrolling
return [ posX, posY ];
}
oElement = getNextAncestor(originalElement);
while( oElement && oElement != document.body && oElement != document.documentElement ) {
posX -= oElement.scrollLeft;
posY -= oElement.scrollTop;
oElement = getNextAncestor(oElement);
}
return [ posX, posY ];
} else {
return [ oElement.x, oElement.y ];
}
}
How the various DOM parts of this work will be covered in later chapters of this tutorial.
DHTML
One of the most popular uses of JavaScript is DHTML (Dynamic HyperText Markup Language). Strictly speaking, DHTML is using JavaScript to modify the CSS styles of HTML elements. All current browsers support much more than I will describe at this stage of the tutorial. For now, I will concentrate on the capabilities introduced by 4th generation browsers. Old browser versions (such as Netscape 4 and Opera 6-) limit the abilities of '4th generation' DHTML, as these can only handle basic DHTML. However, they still provide easily enough flexibility to make features like pop-up menus, message scrollers, mouse trails or falling snow effects.
This requires parts of the page to move or appear/disappear. In current browsers, this can be done with any element, and in older browsers can be done reliably using the div element and in many cases the span element, when they are positioned using the position: absolute; or position: relative; style (except when changing the display style). Personally, I almost always use the div element and I can guarantee that it will work with everything I show you here. Some very old browsers (particularly Netscape 4) have problems using the span element when positioned absolutely.
Once you have set up the HTML (using a few workarounds for bugs), you can reference the positioned element. You can then use that reference to change the visibility, position, background colour, z-index, clipping and size of the positioned element. You can even rewrite the contents of the positioned element or create new positioned elements after the page has loaded. DHTML also allows you to change the background colour of the whole document and change the display style of ANY element.
Sadly, DHTML is always browser specific, in other words different browsers require different commands (though with current browsers supporting the DOM, it is often possible to use only that approach in current Web sites). Using the techniques I will show you, this is not difficult to work with, to make sure that no matter what syntax the browser expects, the script can check what to use, and use it correctly. Many authors make the mistake of detecting browsers and assuming capabilities, or detecting one capability and assuming another. This tutorial does not rely on these mistaken assumptions. At no point should you need to detect specific browsers.
4th generation browsers
There are several browsers that I know of that can handle DHTML (often called 'version 4' or '4th generation' browsers, even though their version number may not actually be as high or low as 4). The following list gives a different line for each significantly different browser. Some of these browser engines are used in a large number of other browsers. For example:
- 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 on a large number of mobiles and devices, in a variety of different applications.
- The KHTML/Webkit engine is used in Chrome, OmniWeb and several other browsers.
Major browsers
- Internet Explorer 5+ (Win)
- Uses document.getElementById and document.all
- Mozilla Gecko (FireFox, Netscape 6+)
- Uses document.getElementById (also secretly uses document.all)
- Opera 7+
- Uses document.getElementById and document.all (document.all is secretly supported in Opera 9.5+)
- KDE KHTML/WebKit (Safari, Chrome, Konqueror 3+, OmniWeb 4.5+)
- Uses document.getElementById (Chrome/Safari 3 also secretly uses document.all)
Minor browsers
These browsers are not used very much, or are no longer the current versions of these browsers. Some may have significant bugs, limited capabilities, or limited event detection.
Several of these use the Mozilla Organization's Java based Rhino JavaScript engine to power their browsers. The Rhino JavaScript engine only powers the JavaScript language interpreter, it does not govern how JavaScript interacts with the web page. Each browser will have its own DHTML (or DOM) implementation.
- (Internet) Explorer 5 (Mac)
- Uses document.getElementById and document.all
- iCab 3
- Uses document.getElementById and document.all
- Internet Explorer 4 (Win)
- Uses document.all
- (Internet) Explorer 4 (Mac)
- Uses document.all
- Opera 5-6
- Uses document.getElementById
- Mozilla Rhino engine (ICEbrowser)
- Uses document.getElementById and document.all
- Mozilla Rhino engine (Escape/Evo 5)
- Uses document.getElementById and document.all
- Mozilla Rhino engine (Escape 4)
- Uses document.layers
- Mozilla Rhino engine (Clue browser)
- Uses document.getElementById and document.all
- Tkhtml+EES (Tkhtml Hv3)
- Uses document.getElementById
- Netscape 4
- Uses document.layers
- OmniWeb 4.2-
- Uses document.layers (also optionally supports document.all)
- iCab 2
- Uses document.getElementById and document.all
- Konqueror 2
- Uses document.getElementById and document.all
Television or mobile device browsers
Due to the limitations of the devices that these browsers run on, most will have limited event detection. Several popular browsers engines are also used in mobile/TV browsers, including Opera, WebKit and Gecko. Others use their own engines, and are given below:
- NetFront
- Uses document.getElementById
- Pocket Internet Explorer
- Uses document.all and document.getElementById in newer versions
- Netgem 4 browser (NetBox)
- Uses document.getElementById
- OpenTV
- Uses document.getElementById
- iPanel MicroBrowser with advanced modules
- Uses document.getElementById
- WebTV (MSN TV) Plus and extended Classic
- Uses document.all (and non-nested layers alternative)
More information
For more details of these browsers, and to download them, please see my list of '4th+ generation' browsers.
(You can also try this site for a fairly complete list.)
While many JavaScript and DHTML functions worked in Konqueror 2, the browser was unable to detect and handle events properly so most DHTML applications did not work. Konqueror 3 is a big improvement and is now a highly capable browser, so I have given compatibility notes for Konqueror 3+ only.
I am unsure of the abilities of NetBox, iPanel MicroBrowser and OpenTV as I do not have them to test. The NetBox site claims partial CSS 2, so I assume CSS positioning, and W3C DOM 1, so I assume DHTML. OpenTV claims absolute CSS positioning and DOM DHTML. The EIS claims that one of their advanced modules for iPanel MicroBrowser can handle DOM. They might not support innerHTML because that is not part of the DOM standard, even though it has been widely accepted by almost all DOM browsers.
Note that even though Escape 4 is a layers browser, like Netscape 4, it supports both the Netscape syntax and the style object for changing element style, with the exception of clipping. WebTV 2.8+ also supports both syntaxes, but is a proprietary DOM browser.
Setting up the HTML
There are two types of positioning that are of interest here. These are absolute and relative. Absolute allows you to position an element anywhere you want to, in relation to the page. Relative positions the element where it normally would be, and offsets it by whatever amount you specify.
Most DHTML is done with absolutely positioned elements. Relatively positioned elements do not accept the 'clip' style and do not allow their clipping to be changed. The useful feature of relatively positioned elements is that they become containers for absolutely positioned elements. So if you put an absolutely positioned element inside a relatively positioned element, its position will be in relation to the relatively positioned element, and not the whole page.
You can create an absolutely positioned elements using this:
<div id="mydiv" style="position: absolute; left: 10px; top: 120px;">contents</div>
And you can create an relatively positioned elements using this:
<div id="mydiv" style="position: relative; left: 10px; top: 120px;">contents</div>
The ID is important, because that is what will be used to reference the positioned element. Layers browsers call these positioned elements 'layers'.
Bugs / workarounds
If using document.write to create the positioned element, Netscape 4 will fail to load the page. Netscape 4 is not
important any more, so I suggest you ignore it. If you really need to support it, you can use the unofficial
<layer ...>
tag instead of an absolutely positioned div, and the unofficial
<ilayer ...>
tag instead of of a relatively positioned div. Use
if( document.layers )
to find out if you need to.
Similarly in Netscape 4, setting any styles using inline style attributes on elements inside a positioned element will cause the page not to load. Instead, create a class for the elements and style them in the main stylesheet.
Referencing the positioned element
- 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 W3C DOM. It is supported well enough by all current browsers. There are also two alternatives for the few old browsers that may still be in use. One of these techniques has two different versions.
DOM
DOM browsers use the document.getElementById method to reference the element. You can use a simple if statement to check for the existence of this method. In all current browsers, this will be supported, so in the majority of current scripts, you can simply assume it is supported, and ignore the other approaches.
if( document.getElementById ){
var myReference = document.getElementById('divID');
}
Proprietary DOM
Proprietary DOM browsers use the document.all collection to reference the element. Some browsers support both the DOM and proprietary DOM for referencing elements.
if( document.all ){
var myReference = document.all['divID'];
}
Layers
Layers browsers use the document.layers collection to reference the element. Layers in the layers collection can be accessed using either their ID or their numerical index. They will also be able to reference the element through document.divID (where divID is the id that you gave the div). Netscape 4 was the main layers browser, and was the first browser capable of performing DHTML. However, the layers approach has been abandoned by current browsers.
if( document.layers ){
var myReference = document.layers['divID'];
}
Layers also have one behaviour that sets them apart from the other approached, and can make them difficult to write for. If a positioned element is inside another positioned element, the inner one does not appear inside the layers collection of the document. Instead, it appears inside the layers collection for the document object of the outer layer. This means that that to reference any layer by its ID, you must recursively step through every layers collection in the document, until you find the right one. Since ID attributes must be unique within a document anyway, this approach is needlessly convoluted, which is one of the reasons why it was abandoned.
Combining the approaches
The three techniques can be combined into a single function, that will use the DOM method if it is available. Failing that, it will use the proprietary DOM, and failing that, layers. If it fails to find it in the document's layers collection, it steps through the entries in the collection, and checks the layers collection of each of those, and continues recursively.
function getRefToDiv(divID,oDoc) {
if( document.getElementById ) {
return document.getElementById(divID); }
if( document.all ) {
return document.all[divID]; }
if( !oDoc ) { oDoc = document; }
if( document.layers ) {
if( oDoc.layers[divID] ) { return oDoc.layers[divID]; } else {
//repeatedly run through all child layers
for( var x = 0, y; !y && x < oDoc.layers.length; x++ ) {
//on success, return that layer, else return nothing
y = getRefToDiv(divID,oDoc.layers[x].document); }
return y; } }
return false;
}
Changing the visibility
- 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
style property. You can then set the relevant value:
So, after all of that, a full set of instructions to make a positioned element visible would be:
function showDiv(divID_as_a_string) {
//get a reference as above ...
myReference = getRefToDiv(divID_as_a_string);
if( !myReference ) {
window.alert('Nothing works in this browser');
return; //don't go any further
}
//now we have a reference to it
if( myReference.style ) {
//DOM & proprietary DOM
myReference.style.visibility = 'visible';
} else {
//layers syntax
myReference.visibility = 'show';
}
}
showDiv('myDiv');
... ...
<div style="position:absolute;left:0px;top:0px;" id="myDiv">
contents
</div>
Test it here: show the hidden element.
This positioned element was hidden. Hide it again.
Of course, don't actually alert the message 'Nothing works in this browser', instead, offer them an alternative that does not require the fancy script.
Changing the position
- 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 and top styles must have been set using the inline style syntax, or by being initially set with JavaScript.
When a strict doctype is used, many browsers require you to use the correct units (20px, not 20), but many older browsers do not understand this, such as Opera 5 ('top' only), layers browsers, WebTV etc. There is no direct way to detect if a browser understands the units or not, since it may accept the values, even if it does not support them. For this reason, I detect document.childNodes, as this is one of the basic requirements for proper DOM support. DOM style requires units to be used, and therefore all browsers that understand DOM also understand the units.
In this example, left and top are both initially set to 10px using an inline style attribute. The code will check if
it needs to use a '.style
' reference, and if so, it will change the reference the style object instead,
allowing the style to be changed for all versions in just one step. It will then retrieve the current positions,
add on the new amount and set that as the new position. When it retrieves the current value, it uses parseInt, because
the value may contains the units, depending on the browser.
<div style="position:absolute;left:10px;top:10px;" ...
... ...
var noPx = document.childNodes ? 'px' : 0;
if( myReference.style ) { myReference = myReference.style; }
myReference.left = ( parseInt(myReference.left) + 10 ) + noPx;
myReference.top = ( parseInt(myReference.top) + 20 ) + noPx;
The div will move 10 pixels down, and 20 pixels to the right. It will now be at coords - top: 30px; left: 20 px;
Test it here: move this element.
This can quite easily be used along with a timer which fires every few
milliseconds and repositions a positioned element according to the
scrolling offset to make the element appear static on the screen
(position:fixed;
is preferred, however).
Changing the background colour
- 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. The z-index should be a positive integer. The higher the z-index, the more positioned elements it will be stacked on top of. Two elements must not be given the same z-index. To read the z-index of a positioned element, it must be already defined using the inline style syntax.
if( myReference.style ) { myReference = myReference.style; }
myReference.zIndex = 100;
Test it here: Click the link in each positioned element to raise its z-index above the z-index of the other element.
Changing the clipping
- 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 if myReference.innerHTML and myReference.clip are supported. I do not know of any browsers that should have problems with this as they support innerHTML but not clipping, but do give 'rect()' if asked for the clip style. However, setting the clipping in browsers that do not support it will not cause errors, it just has no effect. As Escape supports the style object but does not support the style.clip syntax, it is important to make sure it uses the clip object instead, so this is put first.
if( myReference.clip ) {
myReference.clip.left = 0;
myReference.clip.top = 0;
myReference.clip.right = 10;
myReference.clip.bottom = 10;
} else if( myReference.style ) {
//top right bottom left
myReference.style.clip = 'rect(0px,10px,10px,0px)';
} else {
//FAILURE, nothing works
}
Test it here: select a clipping rectangle for the positioned element below. The background shows through wherever the clipping hides parts of the element:
Line one of the positioned element. Your browser does not support clipping.
Line two of the positioned element.
Background Background Background
Background Background Background
Note that although the CSS 2.1 standard requires browsers to support rect()
with commas between
parameters, Internet Explorer 6 and 7 do not apply the rule if it contains commas, if the page's DOCTYPE triggers
"standards rendering mode". (This is due to a mistake in the CSS 2.0 specification.) Note that
this only applies when the style is set with a stylesheet; if set with a script, it will work with the correct syntax.
To avoid this problem, simply set the style twice in the stylesheet, once with commas, and once without. Browsers will
use whichever version they understand.
Changing the size
- 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:
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.
This functionality has been completely replaced in the W3C DOM, using various methods. The old DHTML approach should not be used in any new Web pages, and is best removed and replaced with DOM functionality (or innerHTML, which has now been standardised, and supported by all current browsers). It is retained here to allow it to be recognised.
In the following example, myReference is obtained as before. Most browsers provide the innerHTML property, which you can rewrite with the new HTML. Layers browsers allow you to use the methods open, write and close on the document object of the layer. Some browsers (like Opera) will also provide the document property of the positioned element, but that is a reference back to the main document. Rewriting that will cause no end of problems so we must check that this is not the case first.
if( typeof( myReference.innerHTML ) != 'undefined' ) {
//used by all current browsers
myReference.innerHTML = 'some <b>new</b> content';
} else if( myReference.document && myReference.document != window.document ) {
//used by layers browsers
myReference.document.open();
myReference.document.write('some <b>new</b> content');
myReference.document.close();
}
It is also possible to extend support to Opera 5 and 6 if needed, by providing an iframe (which should be given a name attribute), checking if it has loaded, then rewriting its contents:
if( typeof( myReference.innerHTML ) != 'undefined' ) {
//used by all current browsers
myReference.innerHTML = 'some <b>new</b> content';
} else if( myReference.document && myReference.document != window.document ) {
//used by layers browsers
myReference.document.open();
myReference.document.write('some <b>new</b> content');
myReference.document.close();
} else if( window.frames && window.frames.length && window.frames['nameOfIframe'] ) {
//used by browsers like Opera 6-
myReference = window.frames['nameOfIframe'].window;
myReference.document.open();
myReference.document.write('some <b>new</b> content');
myReference.document.close();
}
Test it here: rewrite the contents of the positioned element below (the Opera 5-6 workaround is not included).
This is the positioned element. The contents have not yet been rewritten.
Creating new positioned elements
- 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.
This functionality has been completely replaced in the W3C DOM, using createElement. The old DHTML approach should not be used in any new Web pages, and is best removed and replaced with DOM functionality. It is retained here to allow it to be recognised.
As the page is loading, we can create new positioned elements using document.write, or just regular HTML. Once the page has loaded, 5th generation browsers allow us to create any new elements, but some 4th generation browsers also allow us to create elements after the page has loaded.
Most browsers allow the innerHTML property of an element to be manipulated (the Internet Explorer series also provide insertAdjacentHTML, but most other DOM browsers do not include it). Layers browsers (with the exception of Omniweb 4.2-) provide a constructor that creates new empty layers, whose contents can then be written. Before trying to use this constructor, we must check it exists. Although OmniWeb 4.2- provides the Layer constructor, using it causes errors, so I use document.classes to isolate OmniWeb 4.2- and protect it from the script.
With the layers syntax, the new layer is entered into the layers collections using its numerical index but is not entered into the collection with a name, so we can add it there ourselves if we want to. We also have to specify a nominal maximum width. It will be positioned differently in different layers browsers, so we have to position it exactly ourselves. It will be hidden, so we must show it. With the innerHTML syntax, we can create ANY new content, but to be cross browser, I will create a positioned element.
Opera 7-7.1 incorrectly strips all existing tags in the parent element of their style attributes when reading innerHTML, producing an odd effect. To combat this, I use insertAdjacentHTML if the browser provides it, as this does not produce problems.
This first example will create a new positioned element as a child of the document.
if( document.layers && window.Layer && document.classes ) {
//create a layer 350px wide
document.layers['newName'] = new Layer( 350 );
//write its content
document.layers['newName'].document.open();
document.layers['newName'].document.write('new content');
document.layers['newName'].document.close();
//style it
document.layers['newName'].left = 0;
document.layers['newName'].top = 0;
document.layers['newName'].visibility = 'show';
} else if( document.body ) {
var theString = '<div style="position:absolute;left:0px;top:0px;' +
'width:350px;">new content</div>';
if( document.body.insertAdjacentHTML ) {
document.body.insertAdjacentHTML( 'beforeEnd', theString );
} else if( typeof( document.body.innerHTML ) != 'undefined' ) {
document.body.innerHTML += theString;
} else {
//FAILURE, nothing works
}
} else {
//FAILURE, nothing works
}
Th next example will create a new positioned element as a child of an existing positioned element (myReference is obtained as above):
if( document.layers && window.Layer && document.classes ) {
//create a layer 350px wide
document.layers['newName'] = new Layer( 350, myReference );
//write its content
document.layers['newName'].document.open();
document.layers['newName'].document.write('new content');
document.layers['newName'].document.close();
//style it
document.layers['newName'].left = 0;
document.layers['newName'].top = 0;
document.layers['newName'].visibility = 'show';
} else {
var theString = '<div style="position:absolute;left:0px;top:0px;' +
'width:350px;">new content</div>';
if( myReference.insertAdjacentHTML ) {
myReference.insertAdjacentHTML( 'beforeEnd', theString );
} else if( typeof( myReference.innerHTML ) != 'undefined' ) {
myReference.innerHTML += theString;
} else {
//FAILURE, nothing works
}
}
Test it here: create a new element (new elements will alternate in colour and will be slightly offset from each other to make it more easy to see what is happening).
This is the original content of the existing positioned element. It will not be changed.
Changing the background colour of the whole document
- 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.
- Older browsers such as 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.
All current browsers support the display style, and can be told to display any element like an inline element
(display:inline;
), like a block element (display:block;
), or not to display it at all(display:none;
).
Most also support many other display styles. However, some older browsers do not allow the display style to be changed with JavaScript,
so if the display style is initially set to 'none' and then you attempt to set it to 'block' or '', the display style will
not actually change, and the content will remain invisible. This is also the case for browsers where JavaScript is disabled.
For this reason, it is best to change the display style with script after the page has loaded. Browsers that do not understand it will just ignore you, and allow the user to view the content. It may not be as neat as being able to show and hide it, but at least they will be able to see it.
The display style is different from the visibility style. Setting the visibility style to 'hidden' will hide the element from view, but will leave the space for it. Setting the display style to 'none' will remove it from view and will re-arrange the contents of the page to fill the hole. This can make the feature very useful for menu systems, as with very simple programming, the browser can take care of all the re-arranging to make the page look nice again.
Browsers that allow elements to have their display style changed will all use document.getElementById or document.all, not document.layers. This makes life slightly easier. Of course, it is nice to be able to tell users that this will not work in their browser, so we will want to detect if the browser allows us to change the display style first. To do this, we must initially set the display style using inline style sheets, with it set the the element's default display ('inline' for elements like spans, and 'block' for elements such as paragraphs or DIV).
The HTML
In this example, I will have a span inside a paragraph:
<p>This is a paragraph.
<span style="display:inline;" id="aUniqueId">This is a span
within the paragraph whose display style has been set to
'inline' using an inline style sheet.</span> This is another
sentence in the paragraph, but not in the span.<p>
The JavaScript
function changeDisplay( elementId, setTo ) {
var theElement;
if( document.getElementById ) {
//DOM
theElement = document.getElementById( elementId );
} else if( document.all ) {
//Proprietary DOM
theElement = document.all[ elementId ];
}
if( !theElement ) {
/* The page has not loaded, or the browser claims to
support document.getElementById or document.all but
cannot actually use either */
return;
}
//Reference the style ...
if( theElement.style ) { theElement = theElement.style; }
if( typeof( theElement.display ) == 'undefined' ) {
//The browser does not allow us to change the display style
//Alert something sensible (not what I have here ...)
window.alert( 'Your browser does not support this' );
return;
}
//Change the display style
theElement.display = setTo;
}
Try it here: select a display style for the span in the paragraph below:
This is a paragraph. This is a span within the paragraph whose display style has been set to 'inline' using an inline style sheet. This is another sentence in the paragraph, but not in the span.
Element contents
The DOM provides an easy way to reference all elements, no matter where they are in a document. However, it is possible to reference many elements in a way that works in DOM browsers, and older browsers. This can be useful if you need your scripts to work in browsers like Netscape 4, even though they are not used any more.
Layers browsers like Netscape 4 are different to all other browsers. They give each positioned element a new document object. All forms, images, links and child layers inside that layer, must be referenced through this new document object. So instead of document.formname, it becomes myReference.document.formname, etc.
How to reference anything in a generic way
MacroMedia first came up with a generic reference-all function for use in their 'wysiwyg' page editors. It could reference images and forms and all types of layers. This would look for any object with a name or id that matched the required name. Inspired by that, I have written my own (completely separately) that could also reference a few other things. It is a little larger as a result.
The only thing it cannot reference is a link object, because due to restrictions in older browsers, link objects are only accessible using their numerical index. Also, unlike anchor objects, the name attribute is not accessible with link objects, even if they have one. If referred to using the anchors collection, the link properties are not available, although the name attribute is. By checking every anchor to see if its name matches the requested name, I can reference the correct anchor. However, I cannot reference the link.
Note that if the name refers to inputs in a form that share the same name, this function will return the collection of inputs sharing that name.
function MWJ_findObj( oName, oFrame, oDoc ) {
/* if not working on a layer, document should be set to the
document of the working frame
if the working frame is not set, use the window object
of the current document
WARNING: - cross frame scripting will cause errors if
your page is in a frameset from a different domain */
if( !oDoc ) { if( oFrame ) { oDoc = oFrame.document; } else { oDoc = window.document; } }
//check for images, forms, layers
if( oDoc[oName] ) { return oDoc[oName]; }
//check for pDOM layers
if( oDoc.all && oDoc.all[oName] ) { return oDoc.all[oName]; }
//check for DOM layers
if( oDoc.getElementById && oDoc.getElementById(oName) ) {
return oDoc.getElementById(oName); }
//check for form elements
for( var x = 0; x < oDoc.forms.length; x++ ) {
if( oDoc.forms[x][oName] ) { return oDoc.forms[x][oName]; } }
//check for anchor elements
//NOTE: only anchor properties will be available,
//NOT link properties (in layers browsers)
for( var x = 0; x < oDoc.anchors.length; x++ ) {
if( oDoc.anchors[x].name == oName ) {
return oDoc.anchors[x]; } }
//check for any of the above within a layer in layers browsers
for( var x = 0; document.layers && x < oDoc.layers.length; x++ ) {
var theOb = MWJ_findObj( oName, null, oDoc.layers[x].document );
if( theOb ) { return theOb; } }
//check for frames, variables or functions
if( !oFrame && window[oName] ) { return window[oName]; }
if( oFrame && oFrame[oName] ) { return oFrame[oName]; }
//if checking through frames, check for any of the above within
//each child frame
for( var x = 0; oFrame && oFrame.frames && x < oFrame.frames.length; x++ ) {
var theOb = MWJ_findObj( oName, oFrame.frames[x], oFrame.frames[x].document ); if( theOb ) { return theOb; } }
return null;
}
without comments, this becomes:
function MWJ_findObj( oName, oFrame, oDoc ) {
if( !oDoc ) { if( oFrame ) { oDoc = oFrame.document; } else { oDoc = window.document; } }
if( oDoc[oName] ) { return oDoc[oName]; } if( oDoc.all && oDoc.all[oName] ) { return oDoc.all[oName]; }
if( oDoc.getElementById && oDoc.getElementById(oName) ) { return oDoc.getElementById(oName); }
for( var x = 0; x < oDoc.forms.length; x++ ) { if( oDoc.forms[x][oName] ) { return oDoc.forms[x][oName]; } }
for( var x = 0; x < oDoc.anchors.length; x++ ) { if( oDoc.anchors[x].name == oName ) { return oDoc.anchors[x]; } }
for( var x = 0; document.layers && x < oDoc.layers.length; x++ ) {
var theOb = MWJ_findObj( oName, null, oDoc.layers[x].document ); if( theOb ) { return theOb; } }
if( !oFrame && window[oName] ) { return window[oName]; } if( oFrame && oFrame[oName] ) { return oFrame[oName]; }
for( var x = 0; oFrame && oFrame.frames && x < oFrame.frames.length; x++ ) {
var theOb = MWJ_findObj( oName, oFrame.frames[x], oFrame.frames[x].document ); if( theOb ) { return theOb; } }
return null;
}
This function can be called in two ways; to reference an item in the current document:
var theOb = MWJ_findObj( NameOrId );
To reference an item in any other document in a frameset, you can choose how high in the frame structure to start. Remember that if using 'top', someone else may load your page in their frameset, and this will produce an error.
var theOb = MWJ_findObj( NameOrId, ReferenceToTopMostFrameToSearch );
Window size and scrolling
Finding the size of the browser window
- 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 not Internet Explorer 8-, and even in Internet Explorer 9+, it is not available in quirks mode.
- document.body.clientHeight/Width
- Provided by many browsers, including Internet Explorer.
- document.documentElement.clientHeight/Width
- Provided by most DOM browsers, including Internet Explorer.
This is a little messy because the clientHeight/Width properties can mean different things in different browsers, and even different things in the same browser, depending on whether the document type declaration triggers the browser's strict mode or quirks mode. In some cases, they refer to the dimensions of the window, and sometimes they refer to the dimensions of the contents of the document. The table below shows what the properties mean in different browsers, and different modes:
Browser | window. innerHeight |
document. body. clientHeight |
document. documentElement. clientHeight |
---|---|---|---|
Opera 9.5+ strict | window | document | window |
Opera 9.5+ quirks | window | window | document |
Opera 7-9.2 | window | window | document |
Opera 6 | window | window | N/A |
Mozilla strict | window | document | window |
Mozilla quirks | window | window | document |
Newer KHTML | window | document | window |
Older KHTML | window | document | document |
Chrome/Safari 4+ | window | document | window |
Safari 3- | window | document | document |
iCab 3 | window | document | document |
iCab 2 | window | window | N/A |
IE 9+ strict | window | document | window |
IE 6-8 strict | N/A | document | window |
IE 5+ 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 took a long time to make up its mind, and its influence means that other browsers change their clientHeight behaviour in different versions in order to match it. For now, almost all browsers provide window.innerHeight/Width so that can be used. The other values may swap the values around when the browsers are in strict/quirks mode. Fortunately, the only browser that does not provide innerHeight, Internet Explorer 8- (and 9+ in quirks mode), gives 0 in quirks mode for the root element's clientHeight. This means that if we see a value on the documentElement's properties, and the browser does not provide the properties on the window object, we can assume it is Internet Explorer 6-8 in strict mode.
The most accurate method I could come up with uses the following algorithm:
- If window.innerHeight/Width is provided, that is fully trustworthy, use that (Hooray!).
- Else if document.documentElement.clientHeight/Width is provided and either one is greater than 0, use that.
- Else use document.body.clientHeight/Width.
This algorithm will fail with IE 6-8 in 'standards compliant mode' and all versions in quirks mode if the window is shrunk to 0px by 0px. This is only possible when the user actively shrinks the window to hide the page within it. Even then, it will just give the height of the document instead, so it should not be a problem. The following code performs the algorithm as described.
function alertSize() {
var myWidth = 0, myHeight = 0;
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
window.alert( 'Width = ' + myWidth );
window.alert( 'Height = ' + myHeight );
}
Test it here: get the inner dimensions of this window.
Note that browsers may take the width either inside or outside the scrollbar (if there is one). In most current browsers, you can ensure that your page triggers standards mode, then use documentElement.clientHeight to get the height inside any scrollbar. window.innerHeight can be used to get the height outside any scrollbar, but that is not possible in Internet Explorer 8-. It is a little messy, and if you do need to make the distinction, you will need to decide which browsers are likely to be important. If you need to reliably get the dimensions inside the scrollbar, including in all current browsers (not IE 6-), you can create a fixed position element with top, right, bottom and left all set to 0, and measure its offsetHeight and offsetWidth.
Finding how far the window has been scrolled
- 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 8- (and 9+ in quirks mode) is the odd one out, as it does not provide these properties. Internet Explorer and some other browsers will provide document.body.scrollLeft/Top. In strict mode, IE 6+ and a few other browsers, provide document.documentElement.scrollLeft/Top.
If the scrollLeft/Top properties are provided on either the document.body or document.documentElement, they are reliable in everything except older Safari and OmniWeb 4.5+, which return -8 if the scrolling is 0, but get all other scrolling offsets correct. However, as they correctly provide window.pageXOffset/pageYOffset, this script will not have any problems.
The following script will obtain the scrolling offsets.
function getScrollXY() {
var scrOfX = 0, scrOfY = 0;
if( typeof( window.pageYOffset ) == 'number' ) {
//Netscape compliant
scrOfY = window.pageYOffset;
scrOfX = window.pageXOffset;
} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
//DOM compliant
scrOfY = document.body.scrollTop;
scrOfX = document.body.scrollLeft;
} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
//IE6 standards compliant mode
scrOfY = document.documentElement.scrollTop;
scrOfX = document.documentElement.scrollLeft;
}
return [ scrOfX, scrOfY ];
}
Test it here: get the scrolling offsets.
Event information
As with DHTML, this is always browser specific. When an event is triggered, the browser keeps some information about the event that we can use. There are two ways that browsers use to allow us to access this information. DOM compatible browsers (including Internet Explorer 9+ in standards mode) pass the event information to the event handler function as the first argument. Some older browsers also use this version.
Internet Explorer and a few other browsers store the event information in an event register, which we can access by writing 'window.event'. Internet Explorer versions that support the DOM approach disable their support for it in quirks mode, so even if older IE versions do not need to be catered for, scripts that need to run in quirks mode will still need to allow the IE approach. This tutorial covers both approaches, and will work no matter which IE version is used, and no matter which mode the document is rendered in. For practical cases, I recommend continuing to use this approach.
We also have to tell the browser what objects should detect what events and we also have to tell it what to do when that object detects that event.
Although it is no longer used, you may want the script to work in Netscape 4, since it is capable of doing this. In practical use, I recommend against adding Netscape 4 support, and include the Netscape 4 workaround here so that you can learn to recognise it, and possibly remove it from older scripts. With positioned elements and the document itself, Netscape 4 and Escape 4 will need to be told to capture the events first. Some other browsers (such as Mozilla/Firefox/Netscape 6+) may also provide the methods, but they do not actually do anything. Some others provide them but do not know how to use them, so you should check for the specific Event type as well. For example the simplest code used to listen for a keyup event would just be this:
document.onkeyup = alertkey;
//where alertKey is a function that will handle the event
Code designed to work with Netscape 4 will use this instead:
//Only Netscape 4 and Escape 4 need this first line
if( document.captureEvents && Event.KEYUP ) { document.captureEvents( Event.KEYUP ); }
document.onkeyup = alertkey;
//where alertKey is a function that will handle the event
Problems can arise if one element detects an event where a parent element also detects it. For example, if the document is told to detect when the user presses a key, and a text box in the document is also told to detect when the user presses a key, when the user presses a key in the text box, should the document react or the text box? Or both? And in what order? Some browsers will use capturing to say which element(s) should detect the event and in what order, while some will use bubbling, and many will do neither. I will not describe either of these here as they are far beyond the scope of this stage of the tutorial. I will cover them later in the DOM events part of the tutorial.
For a full list of events that elements can detect cross-browser, see the section on The JavaScript object' subsection 'Standard document components. To see what events can be captured using captureEvents, see the same section, subsection 'window.Event. To see what information is passed about events, see the same section, sub section 'Event objects.
The following examples attempt to solve as many problems as possible.
Detecting the keyup event over the page and extracting the key code.
- Various browsers may restrict which non-printing keys fire what events. Chrome/Safari 3.1+ in particular will not fire the keypress event 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 4 gives a different key
code for the letter 'a' to all other browsers. It may be more useful to use
String.fromCharCode(key_code)
which converts the key code back
to its relevant key (like 'G', for example).
//first, tell the browsers to react to the event
if( document.captureEvents && Event.KEYUP ) {
//remove this part if you do not need Netscape 4 to work
document.captureEvents( Event.KEYUP );
}
/* this next line tells the browser to detect a keyup
event over the whole document and when it detects it,
it should run the event handler function 'alertkey' */
document.onkeyup = alertkey;
//now create the event handler function to process the event
function alertkey(e) {
if( !e ) {
//if the browser did not pass the event information to the
//function, we will have to obtain it from the event register
if( window.event ) {
//Internet Explorer 8-
e = window.event;
} else {
//total failure, we have no way of referencing the event
return;
}
}
if( typeof( e.keyCode ) == 'number' ) {
//DOM
e = e.keyCode;
} else if( typeof( e.which ) == 'number' ) {
//NS 4 compatible, including many older browsers
e = e.which;
} else if( typeof( e.charCode ) == 'number' ) {
//also NS 6+, Mozilla 0.9+
e = e.charCode;
} else {
//total failure, we have no way of obtaining the key code
return;
}
window.alert('The key pressed has keycode ' + e +
' and is key ' + String.fromCharCode( e ) );
}
Test it here: Click this link to start / stop keyup detection then press any key to test this script. Note that some keys give VERY odd responses with String.fromCharCode. The only reliable ones are letter and number keys on the main part of the keyboard.
Detecting the mouse coordinates when it moves over a positioned element
- In Internet Explorer 8- (and 9+ in quirks mode), 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- (none of which are the current versions of those browsers) give the coordinates relative to the entire page. There is no easy way to detect if a browser supports it correctly and the only way to write this piece of script is to detect the browsers that do not comply with the standard and provide appropriate scripts. This is the only time I will tell you to do this.
Clue browser also makes this mistake, but as it cannot detect scrolling, there is no need to compensate.
Note that Opera 7+, Konqueror 3+ and iCab 3+ actually comply with the standard, but as they also provides pageX, the script I will show you uses that instead, so again, the problem is avoided.
Opera 6- can be detected because the property 'navigator.userAgent' contains the string 'Opera', even if it is running in IE5 emulation mode. iCab 2- can be detected because its window.ScriptEngine method contains the string 'InScript', even if it is running in emulation mode. Konqueror 2 can be detected because the property 'navigator.vendor' is 'KDE', even if it is running in emulation mode.
- If pageX/Y is supplied, pageX/Y is relative to the whole page, and is completely reliable.
- If clientX/Y is supplied, clientX/Y should be relative to displayed portion of page (DOM compatible).
- Sometimes clientX/Y is relative to the whole page (in browsers that did not implement the specification properly).
Virtually all current browsers provide both pageX/Y and clientX/Y. Internet Explorer 8- (and 9+ in quirks mode) is the only current browser that provides clentX/Y, but not pageX/Y.
See the last section, 'Window size and scrolling', for information on how to detect how far the page has been scrolled. See the section on 'DHTML', for how to reference the positioned element.
if( myReference.captureEvents && Event.MOUSEMOVE ) {
//remove this part if you do not need Netscape 4 to work
myReference.captureEvents( Event.MOUSEMOVE );
}
myReference.onmousemove = alertCoord;
function alertCoord(e) {
if( !e ) {
if( window.event ) {
//Internet Explorer 8-
e = window.event;
} else {
//total failure, we have no way of referencing the event
return;
}
}
if( typeof( e.pageX ) == 'number' ) {
//most browsers
var xcoord = e.pageX;
var ycoord = e.pageY;
} else if( typeof( e.clientX ) == 'number' ) {
//Internet Explorer 8- and older browsers
//other browsers provide this, but follow the pageX/Y branch
var xcoord = e.clientX;
var ycoord = e.clientY;
var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
( navigator.vendor == 'KDE' );
if( !badOldBrowser ) {
if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
//IE 4, 5 & 6 (in non-standards compliant mode)
xcoord += document.body.scrollLeft;
ycoord += document.body.scrollTop;
} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
//IE 6 (in standards compliant mode)
xcoord += document.documentElement.scrollLeft;
ycoord += document.documentElement.scrollTop;
}
}
} else {
//total failure, we have no way of obtaining the mouse coordinates
return;
}
window.alert('Mouse coordinates are ('+xcoord+','+ycoord+')');
}
Test it here: pass your mouse over this link to obtain its coordinates.
To detect mouse coordinates over the whole document, use document instead of myReference.
Since all the problematic old browser versions have now been replaced with versions that work correctly, you may want to remove the sniffer, and keep the code clean. You may not want to do this if there is a chance that any of your visitors are using those older versions (they won't be). If that is not a problem for you, you could use this code instead:
document.onmousemove = alertCoord;
function alertCoord(e) {
var xcoord, ycoord;
if( !e ) { e = window.event; }
if( !e ) { return; }
if( typeof( e.pageX ) == 'number' ) {
xcoord = e.pageX;
ycoord = e.pageY;
} else if( typeof( e.clientX ) == 'number' ) {
xcoord = e.clientX;
ycoord = e.clientY;
if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
xcoord += document.body.scrollLeft;
ycoord += document.body.scrollTop;
} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
xcoord += document.documentElement.scrollLeft;
ycoord += document.documentElement.scrollTop;
}
} else { return; }
window.alert('Mouse coordinates are ('+xcoord+','+ycoord+')');
}
Detecting mouse buttons
- 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 most current browsers when a user activates their context menu, but that is not the same thing, since it also covers Ctrl+Click on Macs, and the context menu key on Windows and Linux/UNIX.
The mouse button is passed using either the which or button properties. With 'which', 1 is left button, 2 is middle button, 3 is right button. With button, the standard says that 0 is left button, 1 is middle button, 2 is right button, but in IE compatible browsers, 1 is left button, 4 is middle button, 2 is right button. Escape/Evo 5 uses a totally different numbering system, 1 is left button, 2 is middle button, 0 is right button - I suggest you ignore Escape/Evo.
The only time you should detect a button is when you want, for example, to create a drag-drop type effect, and you want to ensure they are using the left (normal dragging) button. You should never abuse it to break the user's context menu, as that will only make your site inaccessible, and annoying for your users, and is not reliable anyway. While it is possible in many browsers to detect and replace the user's context menu (for example, to provide a DHTML menu with options relating to the item that the user clicked), you should avoid applying this to the entire document, as many users use their context menu for navigation, and will have problems using your page if you prevent them from opening the browser's context menu.
The event registration attribute (onmouseup="etc."
) is equivalent to an event
handler function, and has access to all the usual function information, such as the attributes collection.
It can get a bit messy writing all of the button detection code in there, so I want to pass
it to the main handler function. Remember that in DOM compatible browsers, the first argument is the event,
so I need to pass that too. I also want the handler to have access to the element that triggered the event
so I must pass that.
<script type="text/javascript">
//link and form elements do not need to be told to capture.
//Using the JavaScript syntax, we have to wait for the relevant
//part of the page to load before telling it to detect the event
//This is easiest done with a load event listener
window.onload = function () { document.links[0].onmousedown=alertBut; }
function alertBut( e, evElement ) {
if( !e ) {
if( window.event ) {
//Internet Explorer 8-
e = window.event;
} else {
//total failure, we have no way of referencing the event
return;
}
}
if( typeof( e.which ) == 'number' ) {
//Netscape compatible
e = e.which;
} else if( typeof( e.button ) == 'number' ) {
//DOM
e = e.button;
} else {
//total failure, we have no way of obtaining the button
return;
}
if( !evElement ) { evElement = this; }
/* 'this' will exist if I have used object.onEventName = alertBut;
If I have passed evElement from the onmouseup attribute,
'this' will refer to window */
window.alert( evElement + ' was clicked with button ' + e );
}
</script>
<a onmouseup="alertBut(arguments[0],this);" href="whatever">
Test this here: click this link and the script will try to work out what button you used.
Note that browsers may prevent you from using JavaScript dialogs (like alert) in right click or middle click event handlers, to prevent you from pointlessly breaking the browser's UI interaction.
There is also an oncontextmenu event supported by most current browsers (not available in all browsers; most obviously not supported in Opera 9-, but supported since Opera 10), that fires instead of the onclick event when the user right clicks, or when they press the context menu key, or otherwise trigger the context menu. This can be cancelled in order to replace the context menu with a custom context menu. Note, however, that the user may be able to prevent it being detected, and disabling it can make your page inaccessible for users who use the context menu for navigation. It is also entirely possible that a browser may not support it (since it is not part of existing standards), and browsers may not always trigger it for all ways of activating the context menu. Use it sparingly, and only for places where you want to provide a custom context menu that provides additional functionality relating to the item that was clicked. It should never be relied on as the only way to provide functionality.
<script type="text/javascript">
function showContextMenu( e ) {
if( !e ) {
//Internet Explorer
e = window.event;
//no need to check for its existence;
//no browser that supports oncontextmenu will fail to provide event information
}
//... put code to get the mouse coordinates here (shown above),
//and show the DHTML layer that will be used as the menu (shown in the DHTML chapter) ...
}
</script>
<a oncontextmenu="showContextMenu(arguments[0]);return false;" href="whatever">
Test this here: right click this link and the script will try to replace the context menu with an alert. (Note that since this attribute is not valid HTML 4, it is set using an event handler property in this test.)
Creating objects
- 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 keyword new
.
When called in this way, the special variable this inside the function references the new object that is being constructed
(it normally refers to the 'current' object, which is usually window, except inside methods). The function should not return a value.
The following function can be used as demonstrated to create an object of class myobject:
function myobject() {
this.containedValue = 0;
this.othercontainedValue = 0;
this.anothercontainedValue = 0;
}
var mything = new myobject();
And there you go, mything is now an instance of class myobject. It will have the following properties, all of which will be 0:
- 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 value someValue.
Use the instanceof operator to find out if your object is an instance of a given object class:
if( myvar instanceof Array ) { ... }
if( myvar instanceof myobject ) { ... }
Creating an object with methods
Now I will give you an example showing you how to create methods for your objects. As an example, I will create a circle as my object. The methods will be
nameOfCircle.retArea()
- Returns the area of the circle (pi r2)
nameOfCircle.retCirc()
- Returns the circumference of the circle (2 pi r)
nameOfCircle.mvBy(xDis,yDis)
- Moves the circle by xDis in the x direction and yDis in the y direction
The following lines point the methods to functions that the object will use as the methods:
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 hasOwnProperty that returns true if the given property is attached to the individual object instance. Of course, it is also possible to check if the object's constructor also has the same property with the same value as the object instance itself, but that can give the wrong result if separate properties exists with the same value on both the object instance and the prototype chain. The hasOwnProperty method accepts a single parameter; the name of the property as a string.
function myclass() {
this.propertytwo = 2;
this.propertythree = 3;
}
myclass.prototype.propertyone = 1;
myclass.prototype.propertytwo = 2;
var myinstance = new myclass();
myinstance.propertyfour = 4;
alert(myinstance.hasOwnProperty('propertyone'));
//alerts false
alert(myinstance.hasOwnProperty('propertytwo'));
//alerts true
alert(myinstance.hasOwnProperty('propertythree'));
//alerts true
alert(myinstance.hasOwnProperty('propertyfour'));
//alerts true
alert(myinstance.hasOwnProperty('propertyfive'));
//alerts false
Public and private properties
This is a concept that is almost never used in JavaScript, and there is a good reason. It simply is not necessary. Even complicated scripts are almost never complex enough to require this level of control. However, many programmers familiar with other programming languages (such as Java or C++) often desire this behavior in JavaScript simply because it is a concept they are familiar with. This sort of concept is only really useful when putting together large projects from multiple pieces of code.
Say for example that I am producing a public functions library. A set of constructors and methods that people can include in their own projects (something that Java programmers use all of the time). Say for example that the mycircle constructor is included in it, so that you can create your own mycircles. Now say for example you try this:
var aCircle = new mycircle(5,6,'about 3mm');
That will work now (it's not a valid measurement, but my constructor will not argue), but later on when you try to use the methods that my class provides, it will fail. So I can check what you have provided, and make sure it is a valid number, and use a default value if it is not. But then you can say something like this:
aCircle.radius = 'some text';
Again, it would break (of course, it is your own fault, but in more complicated applications, it would be possible to make a mistake where this causes a real problem). So what I want to do is to not allow you to modify that property directly, and only allow you to change it using a method that I control:
this.setRadius = function (oRad) {
if( typeof(oRad) == 'number' && oRad >= 0 ) {
this.radius = oRad;
} else {
this.radius = 0;
}
};
An alternative situation where this can be important is if I am storing the information in one set of properties. I then upgrade my libraries, maybe adding some extra functionality. But in order to do so, I have to change the properties that I am using. If your script was relying on them existing in a specific format, and I have now changed that format, your script would fail. If I could protect those properties, and force you to use only methods, then I could do whatever I needed with the properties, and as long as I then change the methods to use the new properties, your script would keep working, and you would not even need to know what I had changed or why. That way, we could each manage our own project, and not waste each other's time.
It would also help avoid possible conflicts between undisclosed properties, and your own scripting. For example, if you decide to temporarily assign a property to an object, but you were unaware that internally, my object constructor already used that property name, you would overwrite my object's property, and cause problems with the object.
This is what private properties are for. They do not allow scripts that use the contructor to use or modify the properties directly. They only allow the methods of the object itself to use and modify them. Unlike many other languages, JavaScript does not declare each variable type as 'public' or 'private'. It all depends on how you create them.
Saying 'this.propertyname
' as I did above will create a public property. Any script can create an object then use and
modify its properties directly. Using 'var' to define a variable in the constructor will create a private property. Note
that unlike public properties, the private properties are then accessed, including from within methods, without the 'this.'
prefix - just like a normal variable. This makes heavy use of JavaScript's scope functionality.
Private variables can only be accessed from methods that are declared inline, and not externally referenced or created using
the prototype construct. Methods of this kind are also known as privileged methods. An example of its use might be:
this.mymethod = function () { alert( propertyname ); };
function myob() {
this.property1 = 'value1'; //this creates a public property
var property2 = 'value2'; //this creates a private property
this.method1 = function () { alert( property2 ); };
}
var oneOb = new myob();
alert(oneOb.property1); //alerts 'value1'
alert(oneOb.property2); //alerts undefined (private property)
oneOb.method1(); //alerts 'value2'
Similarly, you can also create private methods. These are simply a function that you create inside the constructor function. This may look confusing, but it works. The private function can only be called by the constructor itself, or by methods that are defined inline. Private methods can be used as public methods, if they are assigned to a public method constructor, and accessed using the public method constructor (as with 'method2' below).
function myob() {
function cantBeSeen() {
alert(secretValue);
}
var secretValue = '';
this.method1 = function () {
secretValue = 'no surprises';
cantBeSeen();
};
this.method2 = cantBeSeen;
}
var oneOb = new myob();
oneOb.method1(); //alerts 'no surprises'
oneOb.method2(); //alerts 'no surprises'
For more information on private, public and privileged properties and methods in JavaScript, see Douglas Crockford's 'Private Members in JavaScript' page.
Sub-classes and class inheritance
This is also hardly ever used in JavaScript, despite its popularity in other languages. Take this as an example. I want to make a new type of object, that will store the data to represent a sphere (a ball - in case you don't know). Since a sphere is just a three dimensional circle, I would probably want to include all the methods I created earlier for the circle object; retArea to get the cross-section area, retCirc to get the circumference, mvBy to move it by a certain amount. Aditionally, I would probably want retVol to get the volume, and retSurf to get the surface area. I would also need to provide a z coordinate when creating the object, and again when calling the mvBy property.
So, I want to create a new type of object, based on the mycircle, but with a few aditions and modifications. I will call this new type of object a mysphere. Now, I could just rewrite all the code I already wrote for the mycircle and change the bits I need. But in a real-world application, this might be a lot of wasteful duplication, for only one or two modifications. So what I want to do is make the mysphere inherit all the properties and methods of the mycircle, then overwrite only the ones I want.
This effectively makes mysphere a sub-class of mycircle, and making the mysphere class constructor inherit from the mycircle class constructor is as simple as this:
function mysphere(x,y,z,r) { ... constructor code ... }
mysphere.prototype = new mycircle();
In case you are wondering, the way it works is to actually create a mycircle, then assign that to the mysphere constructor prototype. As a result, the mysphere constructor has the mycircle object added to its prototype chain. What this means is that each time a mysphere is created, it will inherit the properties and methods of the mycircle object. It can then override any of these properties using its own prototype, or with properties and methods created within its own constructor. If any of these properties are subsequently removed (using the 'delete' keyword), the inherited ones will become available again. That also means that if the mycircle prototype is changed (properties are added or deleted, etc.), these changes are replicated down the chain, so the mysphere also inherits these changes.
Note that the prototype chain is established for each object as the object is created. Once an object has been created, its prototype chain cannot be changed (though new properties and methods can still be added to its parent classes, and those changes will be reflected in the object). Changes to its prototype chain will have no effect; it will still see the old prototype chain. The new chain will only be used for new instances of that class. This is also the case for the assignment of a new mycircle to the prototype of mysphere (which, as stated, creates a new instance of the mycircle class); any subsequent changes to the prototype chain of the mycircle class will not be reflected in the mysphere class. For this reason, it is very important to ensure that the prototype chain is built in the correct order, with class constructors and their prototypes set up before any child class is made to inherit from them. Properties and methods can still be added to any class or parent class at any time after their own prototype chain has been set up.
The line that makes mysphere inherit from mycircle sounds simple enough. And at least, it would be, but you will remember that mycircle expected me to pass it some parameters that it would then use. Then I call my new constructor, and these parameters will not automatically be passed to the mycircle constructor function. I need to do this myself for each mysphere that I want to create. But since I am not passing these parameters when assigning the mycircle object to the mysphere prototype, I also need to ensure that the mycircle contructor stops before attempting to create its own properties. Instead of defining the public properties immediately, I will define them using a method (that I have decided to call getready), and if the required parameters have been passed (arguments.length), I will immediately call the method.
If the parameters are present, the mycircle constructor keeps working, and no-one needs to know that anything was changed at all. To make things easier later on, I will take all of the methods out of the constructor, and add them later using the prototype. That way, the mycircle prototype methods will always be available without me needing to create a new mycircle. Unfortunately, this also means that public and private properties are very hard (or impossible) to use. It's a trade off - one functionality for another.
function mycircle(x,y,r) {
if( arguments.length ) { this.getready(x,y,r); }
}
mycircle.prototype.getready = function (a,b,c) {
this.xcoord = a;
this.ycoord = b;
this.radius = c;
};
mycircle.prototype.retArea = function () {
return ( Math.PI * this.radius * this.radius );
};
mycircle.prototype.retCirc = function () {
return ( Math.PI * this.radius * 2 );
};
mycircle.prototype.mvBy = function (xDis,yDis) {
this.xcoord += xDis; this.ycoord += yDis;
};
Now, back to the mysphere constructor. I have already said how to make it inherit, but it still needs to run the mycircle.getready method to initialise the properties. To do this, we will need to reference the parent class prototype, and run the method ourselves. This also ensures that even if each parent class uses the same method name (getready), the correct one is always referenced. Since we can almost never be sure how many sub-classes we will need, it is useful to do it this way, and simply use the same method name for each, knowing that we will not have problems with name conflicts.
When running the method, we need to tell JavaScript that even though we are referencing a method for a different prototype, we want to run it as if it were a method of the object we are creating (to make sure that any properties it creates are added to the object we are creating). This could be done using the 'call' method or 'apply' method, but unfortunately, Internet Explorer 5 does not understand these, so I will assign it to a temporary property of the new object, and run it from there.
To reference the method from the parent class prototype, it would be possible to use the constructor's prototype chain to locate it (the property that references the constructor will, at this point, refer to the parent class - more on that later):
this.constructor.getready
However, that will only work for one level of inheritance, due to the way it is run as a method of the current object for all child classes. As a result, it is best to reference it by name, which will work no matter how many levels of class inheritance are used:
mycircle.prototype.getready
Note that when assigning the mycircle object to the mysphere prototype, it also overwrites the mysphere prototype constructor property. This is not a major problem, but some scripts use it, so we will put the reference back where we want it:
function mysphere(x,y,z,r) {
if( arguments.length ) { this.getready(x,y,z,r); }
}
//inherit from the mycircle prototype
mysphere.prototype = new mycircle();
//put the correct constructor reference back (not essential)
mysphere.prototype.constructor = mysphere;
mysphere.prototype.getready = function (a,b,c,d) {
//reference the getready method from the parent class
this.tempReady = mycircle.prototype.getready;
//and run it as if it were part of this object
this.tempReady(a,b,d);
//now that all required properties have been inherited
//from the parent class, define extra ones from this class
this.zcoord = c;
}
mysphere.prototype.mvBy = function (xDis,yDis,zDis) {
//override the existing method
this.xcoord += xDis;
this.ycoord += yDis;
this.zcoord += zDis;
};
mysphere.prototype.retVol = function () {
return ( 4 / 3 ) * Math.PI * Math.pow( this.radius, 3 );
};
mysphere.prototype.retSurf = function () {
return 4 * Math.PI * this.radius * this.radius;
};
And finally, to use it:
var testsphere = new mysphere(3,4,5,6);
alert( 'The cross-section area is ' + testsphere.retArea() );
alert( 'The circumference is ' + testsphere.retCirc() );
alert( 'The volume is ' + testsphere.retVol() );
alert( 'The surface area is ' + testsphere.retSurf() );
Test this class inheritance script.
There is no limit to how many sub-classes can inherit from a class, and there is also no limit to how many levels of sub-classes can be created. As an example, I will create a ball class that will inherit from the mysphere class:
function ball(x,y,z,r,m) {
if( arguments.length ) { this.getready(x,y,z,r,m); }
}
ball.prototype = new mysphere();
ball.prototype.constructor = ball;
ball.prototype.getready = function (a,b,c,d,e) {
this.tempReady = mysphere.prototype.getready;
this.tempReady(a,b,c,d);
this.mass = e;
}
ball.prototype.retDensity = function () {
return this.mass / this.retVol();
};
Test this class inheritance script.
The instanceof operator returns true when testing an object against any class in its prototype chain, so it would say that an instance of the ball class was also an instance of the mysphere class, and also the mycircle class.
For more information on sub-classes and class inheritance in JavaScript, see Kevin Lindsey's class inheritance page.
Static methods and properties
A static property (or method) is a property of the class constructor, not an individual instance of that class.
For example, mycircle.myproperty
instead of (new mycircle()).myproperty
.
While not often used in JavaScript, they are often encountered in other programming languages, and are covered here for those who are familiar with them in other languages. As with most objects in JavaScript, the functions used as class constructors can have properties added to them, and these will be public static properties:
function mycircle() { ... }
mycircle.aStaticProperty = true;
mycircle.aStaticMethod = function () { ... };
Making a private static property is significantly harder, as it is a concept that JavaScript (at least in widely supported implementations) simply does not recognise. However, it is possible to provide an approximate equivalent to this functionality by using an anonymous wrapper function to create a scope where the private "properties" are created as normal variables, then having that return another function that will be used as the constructor. As long as the constructor function is declared inside the anonymous function, it will have access to the private variables from the anonymous function's local scope when it is executed. This structure is described in the section on writing functions.
var mycircle = (function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
function publicConstructor() {
this.normalInstanceProperty = 1;
...
alert(privateStaticVariable);
};
publicConstructor.prototype.normalProperty = 2;
return publicConstructor;
})();
mycircle.publicStaticProperty = true;
mycircle.publicStaticMethod = function () { ... };
Singletons
The singleton is one of the design patterns that is commonly seen in other languages, and in a simplified format within JavaScript. The idea of a singleton is to have a class that can only have one object created from it. Every time a script tries to create another instance of that class, it returns the same copy as before, so if the properties are modified on one of them, then the other will also see the modifications. The use of singletons can be controversial, but I will not cover that here. This tutorial will only show you how to produce them.
In JavaScript, there is the ability to create a generic anonymous object to serve this purpose. It can either be created using new Object()
or the shorthand { ... }
syntax, and can then be given any properties or methods that are needed. However, this does have limitations; using a generic object means that it is not possible to use private properties. In most cases, this is functionality that is not needed, and the majority of scripts simply use a generic object. Instead of trying to constuct new instances of the object, scripts will just create another variable that references the object:
var mySingleton = { foo: 'bar', baz: 'qux', aMethod: function () { ... } };
...
var anotherReference = mySingleton;
If you feel the need for private properties, read on. With other languages, the expectation is to create a class constructor that refuses to be instantiated by other scripts, and provides a public static method that constructs the object the first time it is called, and returns the same object for each subsequent call. JavaScript, however, does not provide a way to prevent a class from being instantiated (at least in widely supported JavaScript). If it exists as a function, it can be called as a constructor.
It is tempting (and sometimes used) to simply use an anonymous function as the class constructor, construct it once, and use its scope to keep variables private. This is limited to normal private properties, and cannot be used for private static properties. The theory is that this enforces the singleton principle by preventing another attempt to reference and use the class constructor. This pattern fails to produce a singleton, however, as other code can simply use the singleton's constructor property to reference it - although you could override the singleton's constructor property, this can cause other problems later on if code needs to be able to use that to add properties to the constructor.prototype.
var mySingleton = new function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
...
};
var newInstance = new mySingleton.constructor();
It is, however, possible to adapt the pattern used for private static properties, to create private properties for the singleton, while at the same time creating a proper singleton. Once again, an anonymous function is used to contain the private variables and functions. However, instead of returning a class contructor function, it just returns the single object:
var mySingleton = (function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
return { ... };
})();
That is, of course, a very simple pattern, but it shows something important; by returning a generic object, it is not possible to use new mySingleton.constructor()
to create a separate instance, as that would just produce a generic object. This particular format is quite awkward, however, as the generic object prevents the prototype from being used to cleanly add more properties and methods. It is very simple to extend it to make that possible, however, just by creating the class contructor function inside the anonymous function:
var mySingleton = (function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
function PrivateConstructor() {}
PrivateConstructor.prototype.publicProperty = true;
...
return new PrivateConstructor();
})();
This now means that new mySingleton.constructor()
will create a new instance of a PrivateConstructor object. However, the constructor function, when called in this way, is pulled out of the inner scope during that call, and as a result, it ends up with a relatively useless object that has none of the prototype's properties. Once again, the singleton principle is enforced. New properties can be created by modifying the mySingleton.constructor.prototype. The singleton can itself be used as the prototype for another constructor, to allow sub-classes to be created.
There are some reasons why this particular approach may not be favoured; specifically that it always creates the singleton even if it is never used. Some may also prefer to use a function call instead of having a global variable always created (though a function is still a global variable), as this may make it possible to change the implementation later to use a different design pattern instead of a singleton. It is possible to take care of both of these concerns, by returning a function instead of an object. That function can check if the singleton has been created yet, store it in a private variable, and return the instance. This has the small limitation that an instance of the object will need to be created by an external script in order to add properties or methods to the prototype, but this is a limitation that is unlikely to be a problem:
var getSingleton = (function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
function PrivateConstructor() {}
PrivateConstructor.prototype.publicProperty = true;
...
var onlyInstance;
return function () {
if( !onlyInstance ) {
onlyInstance = new PrivateConstructor();
}
return onlyInstance;
};
})();
...
var mySingleton = getSingleton();
Or an equivalent approach that assigns the function to a global variable instead of returning it (this may be preferred for clarity, or to allow you to put the class instantiation methods at the top of the anonymous function instead of the bottom of it):
var getSingleton;
(function () {
var onlyInstance;
getSingleton = function () {
if( !onlyInstance ) {
onlyInstance = new PrivateConstructor();
}
return onlyInstance;
};
function PrivateConstructor() {}
PrivateConstructor.prototype.publicProperty = true;
...
})();
It is, however, possible to make the anonymous function return an object which can be used for prototyping directly, but this becomes quite messy. The PrivateConstructor can be made to inherit its prototype from the object that will be returned by the anonymous function. The singleton (when it is created) will then inherit any changes made to that object (in addition, it will inherit the getInstance method from the returned object, but this is harmless). I recommend that you avoid getting into a situation where you would need this functionality in a script.
var singletonMaker = (function () {
var privateStaticVariable = true;
function privateStaticMethod() { ... }
var onlyInstance;
var returnedObject = {
getInstance: function () {
if( !onlyInstance ) {
onlyInstance = new PrivateConstructor();
}
return onlyInstance;
}
};
function PrivateConstructor() {}
PrivateConstructor.prototype = returnedObject;
PrivateConstructor.prototype.publicProperty = true;
...
return returnedObject;
})();
...
singletonMaker.foo = true;
var mySingleton = singletonMaker.getInstance();
alert(mySingleton.foo); //true
For more information on this topic, see Kai Jäger's article about singletons in JavaScript.
ECMAScript 5
ECMAScript 5 adds several additional methods for custom objects, allowing you to control whether properties can be added, or obtain information about the object's prototype. ECMAScript 5 is not currently supported by enough browsers for it to be used in most practical applications. Even the parts that are supported by Internet Explorer 9+ are not available in quirks mode - the browser intentionally regresses to IE 6's scripting support. This means that generic scripts that are designed to work anywhere, will not be able to make use of the new functionality even once it becomes widely supported.
Creating time delays
There are two ways of creating time delays with JavaScript. The first is more simple and will simply wait for a specified amount of time before executing a function. The second does the same but will repeatedly execute the function.
Note, many browsers have a minimum delay length of between 25 and 75 ms, with some of the fastest browsers having a minimum delay of about 3 ms. If a shorter delay is specified, the actual delay will be the minimum delay length. Even with higher numbers, the delay is never perfect. Most browsers will take slightly longer than the time you ask for, typically just a few miliseconds error. Some may correct their errors over time with interval timers. Also note, setting many timers with short delays on one page will cause the browser to become slow and somewhat unresponsive. Three or four timers is usually the reliable limit.
setTimeout
- Internet Explorer only allows two parameters to be passed to setTimeout, additional parameters are ignored.
The first method uses a window method called setTimeout(). The method waits for a specified number of milliseconds then executes the specified code. The code can either be a direct reference to a function, or it can be a string that will be evaluated as if it contained source code.
window.setTimeout(referenceToFunction,timeInMilliseconds);
window.setTimeout('runMoreCode()',timeInMilliseconds);
Wherever possible, you should use a direct function reference as it is much more efficient. Using a string requires the browser to create a new script environment so it can process the script.
If you create a timeout, the code after the call to setTimeout will continue to run as normal. After the specified delay (and once the old thread has finished), the timeout will start a new thread, and the code specified in the call to setTimeout will be run in the new thread, after any code that was still running in the initial thread. Unlike with many more complex languages, JavaScript does not offer any way to control when those threads sleep, wake, or yield. The JavaScript engine handles all of that, and you must accept that your new thread should execute after the end of the current thread. Although in theory, a multi-threaded script engine could execute the new thread at the same time as the old thread, JavaScript engines operate as a single-threaded application, and will simply allow one thread to complete before allowing the other thread to start. The same applies to events, which run in their own threads, and can be triggered at any time - they will wait until existing threads have finished before being started.
To pass variables to a timeout, you can use either format. To use a string, it is necessary to make sure the variables you want to pass can be represented in one of the primitive data types, and do not contain any characters that will break the string format (such as quotes). If possible, you should avoid this format, but this is an example, just in case you need to:
window.setTimeout('runMoreCode(\''+someString+'\','+someNumber+')',10);
The direct function reference is much more easy, as you can pass variables as extra parameters to the setTimeout method. In addition to being more easy, it is also able to accept any type of variable. Note, however, that this does not work in Internet Explorer. Be careful with browsers that use the Mozilla Gecko engine (such as Firefox and Netscape 6+) as they will always pass an extra parameter to the function - the number of miliseconds error. This example uses an inline anonymous function, and is equivalent to using a direct function reference:
window.setTimeout(function (a,b) {
//do something with a and b
},10,someString,someObject);
The variables that are passed to the function will be the values held by the variables at the time that the method was called. For example, assume you had a variable called myVar and it contained the number 5. You then called a timeout, passing it the variable myVar, and set it for a delay of 1 second. Immediately you change the value of myVar to 7. When the timeout fires, it will run the function, and pass it the number 5, since that was the value myVar contained when you called the function.
Unfortunately, since Internet Explorer does not support the functionality to pass additional parameters to a directly referenced timeout function, the most compatible approach is simply to make sure that the variables are kept in the current scope, so that the timeout function can reference them directly.
setInterval
- Internet Explorer only allows two parameters to be passed to setInterval, additional parameters are ignored.
The setInterval method is identical in syntax to setTimeout(). The difference is that as well as firing after the specified delay, it will fire again after the same delay, and will continue to fire at the specified interval until it is cancelled.
window.setInterval(function (a,b) {
//do something with a and b
},10,someString,someObject);
Clearing timeouts and intervals
You can cancel a timeout or interval by calling the relevant clearTimeout or clearInterval method, passing it a reference to the interval object. The code specified in the timer will not be run. In this example, an interval timer will be set to fire once every second (it will perform a useless task of incrementing a number, just as an example), and a timeout will be set to cancel the interval after 3.5 seconds:
var myInterval = window.setInterval(function (a,b) {
myNumber++;
},1000);
window.setTimeout(function (a,b) {
clearInterval(myInterval);
},3500);
You can obtain the timeout object in the same way as shown here for the interval, and you can cancel it in the same way, using clearTimeout.
Using cookies
Cookies are variables that can be stored on a user's computer and be picked up by any other web pages in the correct domain and path. Cookies are set to expire after a certain length of time. They are limited to storing string values only.
Be warned that many users (including me) will not permit cookies on their computers. Do not make your web sites rely on them. The reason for this is that many web sites only use cookies as part of advert tracing systems, which they use to track your movement around the Internet. I would not want anyone to follow me around a city while I was shopping, taking notes of every shop I visit and whether I look in the lingerie section, as that would be an invasion of my privacy. Many people feel the same applies to the Internet. You may find it helps to firstly display a message saying what you are going to use the cookie for, for example to store a username and password for the next time they visit the site.
Note also that European law requires sites to gain explicit permission before using cookies, unless those cookies are essential to the operation of the site (such as a shopping basket).
Some browsers will have a limit to how many cookies they can store, usually 300 cookies or more, of which there may be 20 cookies per domain name. A total of 4 KB (after encoding) of cookies can be stored for any domain or path.
The document.cookie object is a string representation of all cookies available
to the current web page. The document.cookie object is somewhat unusual in that
when you assign string values to it, it does not become
the value that you assign to it. Instead, it takes a part of that value and appends its current
value to that, so making a string containing several pairs of variableName=variableValue
.
Creating / modifying and deleting cookies
I have also written a cookie script to handle all of this for you.
Cookies are created or modified by assigning a name=value
pair to the document.cookie object:
document.cookie = cookieString;
The cookieString is more than just a single value. As well as a value, it can contain other information,
such as when you want the cookie to expire, and what file paths it should apply to. Any of these values that you want
to specify should be put together in a string as keyword=value;keyword=value;etc.
and assigned as a single
string to document.cookie. Only the cookie name and value are required, all other values are optional.
The format of a complete cookie string is:
cookieName=cookieValue[;expires=dataAsString[;path=pathAsString[;domain=domainAsString[;secure]]]]
This is an example of how to set a cookie called mycookie, and assigning it a value of hello, with an expiry date of 17 December 2010, at 10:00 in the morning:
document.cookie = 'mycookie=hello;expires=Fri, 17 Dec 2010 10:00:00 GMT';
- cookieName and cookieValue
- These are strings, and must be URL encoded. They and can contain any characters. To URL encode a cookie name or value, you can use the escape method. Unfortunately, this cannot cope with unicode characters. Newer browsers will also provide the encodeURIComponent method that is able to cope with unicode, but if you use this, you will lose support for older browsers.
- expires
- This must be written in full. The Date.toGMTString() method can return dates in the
correct format for you. For example:
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.var theDate = new Date(); var oneYearLater = new Date( theDate.getTime() + 31536000000 ); var expiryDate = oneYearLater.toGMTString();
- path
- This gives the path or directories that the cookie should be accessible from. The default is the current path. Alter this using '../' (up one directory), '/' starting at the base directory and 'subdirectoryName/' to start from the 'currentDirectory/subdirectoryName/'. NOTE, if two cookies are set with the same name but different paths, both cookies will be permitted. If the script is in a directory where both those paths are satisfied, both cookies are available, and may be returned in any order. There is no way to distinguish between them. This can make scripts difficult to write so be careful!
- domain
- gives the domain that the cookie is accessible from. The default is the current domain. The rules reguarding what may be put here were originally written very strictly. Domain names ending in com, edu, net, org, gov, mil, and int must contain at least 2 '.' characters (eg. www.google.com). Any other domain names must contain at least 3 '.' characters (eg. www.howtocreate.co.uk). The domain should only be the current domain or a subdomain of it. Many browsers will now accept any valid domain name.
- secure
- Having this written means the cookie is only accessible on sites with a secure (https) connection.
Reading cookies
When document.cookie is used to retrieve cookies, they are returned in the following format:
cookieName4=value; cookieName3=value; cookieName2=value; cookieName1=value
Note that the final variable value does not end with a semicolon. If there is no value for a variable, some browsers will give 'cookieName=' but some will only give 'cookieName'. Cookies are only available through the document.cookie object and cannot be accessed by simply typing their name (unless you retrieve the cookie and define that as a variable yourself).
When trying to retrieve values from this string, it is important to ensure that you do not mistake cookies for each other. For example, when searching for 'myname' it is important that you do not find a cookie called 'notmyname' or 'mynamehere'. There are many scripts that get this wrong, but the algorithm I will show you below does not make these mistakes.
The simple way to do this is to use the split
method, to split on the cookie separation value '; '
(including the space). This will
return an array of strings, each of which represents a name=value pair. You can then use a 'for' loop to
cycle through all the array cells, splitting each of them on the '=' string. This returns an array containing
one or two cells. The first will be the cookie name, and the second (if it exists) will be the cookie value.
Use either the unescape or decodeURIComponent methods to decode the contents of
the first cell, containing the cookie name. If that matches the desired name, then you have found the correct
cookie. Use if(!arrayname[1])
to check if the second cell has a value. If not, the cookie's value
is an empty string. If it does have a value, then you can decode it to work out what it is. You can then break
out of the 'for' loop.
If you reach the end of the first array without discovering the required cookie, then the cookie does not exist.
Security
JavaScript is designed as an open scripting language. It is not intended to replace proper security measures, and should never be used in place of proper encryption. See also my article about cross site scripting.
JavaScript has its own security model, but this is not designed to protect the Web site owner or the data passed between the browser and the server. The security model is designed to protect the user from malicious Web sites, and as a result, it enforces strict limits on what the page author is allowed to do. They may have control over their own page inside the browser, but that is where their abilities end.
- JavaScripts cannot read or write files on users' computers (File API - not currently supported by many browsers - allows files to be read by scripts if the user specifically chooses to allow it), though they can cause the browser to load remote pages and resources like scripts or images, which the browser may choose to cache locally. 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 (and Web Storage - similar to cookies but with more data types - in newer browsers).
- 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, but never for anything really worth protecting. If you have something worth protecting, it is worth protecting properly; server-side, with an encrypted connection.
Encrypting data before it is sent to you
Normally, this cannot be done with JavaScript using the Internet alone, and should always be done properly; using a proper SSL connection.
You can, in theory, encrypt text at the user's end and unencrypt it at your end. The problem is that the user has to encrypt it with a password that you know so that you can unencrypt it. They would have to tell you by telephone or post. Alternatively, you could put the password in the source of the page and get the function to encrypt using that key. But this password would have to be sent over the internet in plain text. Even if you did encode it, it would not be too much work for a snooper to crack it. In fact, the encryption could even be broken with brute force techniques. So what do you do?
The best possible technique that could be done with JavaScript alone would be to create a symmetric encryption key using a twin public/private key pair as with techniques such as Diffie-Hellman or SSL, or use an asymetric public/private key pair and encryption technique as with PGP or RSA. The problem is that in order to prevent brute force cracking techniques, these require the browser to handle numbers as high as 2x10600 or higher. JavaScript is just not natively capable of working with numbers as high as this. As yet, I have found no solution to this, although on http://shop-js.sourceforge.net/ there is an algorithm for emulating large number handling, and an example of JavaScript powered RSA. The technique seems to work and takes only a few seconds to create keys, by using complex mathematics and algorithms (look at the source of crypto.js) to emulate large number handling.
Even so, if doing the equivalent of RSA (etc.), it is still not possible for the user to verify your identity as with SSL certificates, so it would be possible for a third party to inject their own code and have the information sent to them instead, without the user's knowledge. For the best security, stick to real SSL.
Protecting your email address
This is one of the very useful things that JavaScript can do. For those that don't understand the problem, I will summarise. Search engines "crawl" the Internet, following the links in pages and requesting other ones so that they can add the pages to their search databases. Using the same technology, spammers crawl the Internet looking for email addresses, whether in mailto: links or just written on the page. These email harvesters are one of the most annoying uses of otherwise useful technologies.
Simply writing your email address on any web page (through newsgroup postings etc) can leave you flooded with unsolicited emails. Many people fall into the trap of replying to these emails asking to be removed from the mailing list, and succeed only in confirming that their email address is valid. The problem is that you may actually want your email address on the page, or a link that automatically opens up a new email to you. There are a couple of steps you can take to prevent the problems with unsolicited emails:
- Use a throw-away email address like a Gmail, 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 like myName@REMOVE_THISmydomain.com or something more obvious like 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, though it is possible. All they usually do is read the text that makes up the page. So if you write your email address with JavaScript, they will not be able to read it. Remember that if you write the email address as a single word, even in the JavaScript, they may still interpret it as an email address, so it helps to break it up a little:
var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( 'My email address is ' + theName + '@' + theDomain );
This outputs: My email address is myEmailName@myEmailDomain.com
You can also use a mailto link:
var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<a href="mailto:' + theName + '@' + theDomain + '">Contact me<\/a>' );
This outputs: Contact me
You could even use a combination of both:
var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<a href="mailto:' + theName + '@' + theDomain + '">' + theName + '@' + theDomain + '<\/a>' );
This outputs: myEmailName@myEmailDomain.com
There is, however, a problem with this approach. It relies on your viewers having JavaScript enabled. Many of your more web-aware viewers will not. In my case, these are often likely to be people who I want to contact me. Fortunately, these viewers are the ones who are likely to understand what to change if you tell them to as I have showed above (in the bullet points). So, you can use a combination of both approaches:
<script type="text/javascript">
var theName = 'myEmailName', theDomain = 'myEmailDomain.com';
document.write( '<p><a href="mailto:' + theName + '@' + theDomain + '">' + theName + '@' + theDomain + '<\/a><\/p>' );
</script>
<noscript>
<p><a href="mailto:myEmailName(replace with @ symbol)myEmailDomain.com">
myEmailName(replace with @ symbol)myEmailDomain.com</a></p>
</noscript>
In your browser, this outputs:
Semicolons
For use of the semicolon in the 'for' loop, see the section on control structures.
If you have managed to get through this whole thing and still not worked out where to put semicolons, here's how to work out where to put them. The basic idea of semicolons is to tell the browser that you have just finished a command. You actually don't need them at all if you put each instruction on a different line (with some exceptions discussed below) but it is good practice to put them in anyway.
It is easier to remember where not to put them. Don't put them immediately after a {
or }
except when creating objects with the {}
syntax or functions with the name = function () {}
syntax. Don't put them on an otherwise blank line. Do not put two in a row. If you write a command or
instruction, you should put a semicolon.
These are examples of where you should put a semicolon:
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'
)
[
0
]
.
innerHTML
+
' was '
alert
(
myP
+
[
'here'
,
'there'
]
[
0
]
)
</script>
Functionally, that is identical to this:
<p>I</p>
<script type="text/javascript">
var myP = document.getElementsByTagName('p')[0].innerHTML + ' was ';
alert( myP + ['here','there'][0] );
</script>
The JavaScript object
This page gives a list of objects, properties, collections and methods for documents and JavaScript components. This view of all properties available from the respective element types is referred to as the JavaScript object.
Some browsers may provide aditional properties or methods but as this stage of the tutorial is intended to teach you how to program so that your scripts work in all the possible (so called 4th generation) browsers, I have given those that are available in all possible browsers or those which have an alternative, which I will also have given. This is the last chapter of this tutorial where I will deal with such a wide range of browsers. In the next few chapters, I will abandon 4th generation browsers, and move on to the much more advanced DOM browsers (also known as 5th generation browsers).
Contents
- 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
- Child object
- Child property
- Child object being accessed through a collection[]
- Event
- 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,
called varName in this case.
To use this structure to refer to any object, property, collection or method, treat each new branch level as a new child level. For example:
window.document.nameOfForm.nameOfInput.defaultValue = 'I love it';
Note, all collections also have the length attribute. Also note, window is often omitted when referring to child objects.
Standard document components
- window or self Refers to the global object of the current Web page - - read-only
- Array Only useful as a constructor - - read-only
- Boolean Only useful as a constructor - - read-only
- closed This will be true if the window is closed - (used with window.open()) - - read-only
- Date Mostly useful as a constructor - - read-only
- parse(string dateAsAString) Date.parse(string dateAsAString) converts a string into a UNIX timestamp (milli) - Accepts almost any valid date and/or time string format
- defaultStatus Refers to the default message displayed in the status bar - Some browsers allow users to disable the ability to write to this toolbar - - read-write
- document windowObject.document refers to the current document - In a positioned element using layers syntax, - positionedElementObject.document refers to the body of the positioned element - - read-only
- bgColor Refers to the default background colour of the document - - read-write - - Layers browsers only - For cross-browser scripting, see also - document.body.style.backgroundColor - document.documentElement.style.backgroundColor
- body Holds information about the BODY element of the current Page - This object may exist without any of its properties existing - - read-only - - Non-layers browsers only
- clientHeight Represents the height of the inside of the browser window in pixels - In most browsers in strict mode, it represents the height of the document area - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.innerHeight - document.documentElement.clientHeight
- clientWidth Represents the width of the inside of the browser window in pixels - In most browsers in strict mode, it represents the width of the document area - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.innerWidth - document.documentElement.clientWdth
- scrollLeft Represents the horizontal scrolling of the document in pixels - Some browsers will give 0 if they are currently providing the scrolling offset via - document.documentElement.scrollLeft - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.pageXOffset - document.documentElement.scrollLeft
- scrollTop Represents the vertical scrolling of the document in pixels - Some browsers will give 0 if they are currently providing the scrolling offset via - document.documentElement.scrollTop - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.pageYOffset - document.documentElement.scrollTop
- style If available, this refers to the style of the BODY element - - read-only
- background Refers to the default background style of the BODY - equivalent to backgroundColor in Opera 6- - Not available in Opera 5 - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.bgColor - document.documentElement.style.background
- backgroundColor Refers to the default background colour of the BODY - Not available in Opera 5 - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.bgColor - document.documentElement.style.backgroundColor
- color Refers to the default colour of the text in the BODY - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.fgColor - document.documentElement.style.color
- cookie String representation of all cookies available to the current web page - This can be used to create and read cookies - - read-write
- documentElement Holds information about the HTML element of the current page - this object may exist without any of its properties existing - - read-only - - Non-layers browsers only
- clientHeight Represents the height of the document area in pixels - In most browsers in strict mode, it represents the height of the inside of the browser window - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.innerHeight - document.body.clientHeight
- clientWidth Represents the width of the document area in pixels - I most browsers in strict mode, it represents the width of the inside of the browser window - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.innerWidth - document.body.clientwidth
- scrollLeft Represents the horizontal scrolling of the document in pixels - Some browsers will give 0 if they are currently providing the scrolling offset via - document.body.scrollLeft - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.pageXOffset - document.body.scrollLeft
- scrollTop Represents the vertical scrolling of the document in pixels - Some browsers will give 0 if they are currently providing the scrolling offset via - document.body.scrollTop - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - window.pageYOffset - document.body.scrollTop
- style If available, this refers to the style of the HTML element - - read-only
- background Refers to the default background style of the HTML element - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.bgColor - document.body.style.background
- backgroundColor Refers to the default background colour of the HTML element - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.bgColor - document.body.style.backgroundColor
- color Refers to the default colour of the text in the HTML element - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - document.fgColor - document.body.style.color
- domain String representation of the domain name holding the document - representations may vary - - read-write in some browsers (allows related subdomains to communicate)
- fgColor Refers to the default colour of the text in the document - - read-write but becomes read-only after the document has loaded - - Layers browsers only - For cross-browser scripting, see also - document.body.style.color - document.documentElement.style.color
- IdOfPositionedElement Refers to a positioned element in a - layers alternative compatible browser - - read-only - - Layers browsers only - For cross-browser scripting, see also - document.layers - document.all - document.getElementById
- lastModified String representation of the last modified date of the document - Representations may vary - May be the current date or the UNIX epoch if not provided - - read-only
- location A reference to window.location
- nameOfForm Refers to a form in the document - - read-only
- action Refers to the action attribute of a form - - read-write
- encoding Refers to the enctype attribute of a form - - read-write
- length The number of inputs - - read-only
- method Refers to the method attribute of a form - - read-write
- name Refers to the name attribute of a form - - read-write
- nameOfInput/textarea/select or nameOfInputsSharingName[int index] Refers to an input, textarea or select - nameOfInput will not work if inputs share a common name, but this - branch does give the children of these inputs referred to using the - nameOfInputsSharingName collection - nameOfInputsSharingName.length gives number of inputs sharing - that name, eg. the number of radio input options - - read-only
- checked Boolean: says if a checkbox or radio button is checked - - read-write
- defaultChecked Boolean: says if a checkbox or radio button will be checked if the form is reset - - read-write
- defaultValue Refers to the default value of the input - This will be used if the form is reset - Some browsers do not give this attribute for buttons of any kind - Different browsers give different responses for select inputs; do not rely on it - - read-write
- disabled Boolean: says if the input is disabled, if the browser supports it - Netscape 4 does not support it and Opera 5 will not change the value - of the attribute unless the page is refreshed in between (this bug can - cause problems) - - read-write
- form Refers back to the form containing the input - - read-only
- length Refers to the number of options in a select input - or the number of inputs sharing that name - - read-only
- name Refers to the name attribute of an input - - read-write
- selectedIndex The option number currently selected in a select input - -1 if none - - read-write in all except Internet Explorer 4
- type Refers to the type attribute of an input - May be in upper or lower case - - read-only except for select where type can specify - if single ('select-one') or multiple ('select-multiple') - options can be selected
- value The current value of an input - Different browsers give different responses for select inputs; do not rely on it - - read-write
- options[int numberOfOptionWithSelectInput] Refers to an option in a select input - - read-write - - selectObject.options.length gives the current number of options - - read-write - - Create a new option using - optionObject = new Option(string text,string value) - Delete an option by setting it to null
- defaultSelected Boolean: says if the input is the one that is selected when the form is reset - - read-write
- index Refers to the number of the option - - read-only
- selected Boolean: says if the option is selected - - read-write
- text Refers to the visible text of an option - - read-write
- value Refers to the value of an option - - read-write
- onblur Fires when the input looses focus - inputObject.onblur = functionName; - - read-write
- onchange Fires when the value of the input changes or a different option is selected - inputObject.onchange = functionName; - - read-write
- onclick Fires when the user depresses then releases a mouse button over the input - or if the input is activated using the keyboard (whereas mouse event - properties are not provided) - inputObject.onclick = functionName; - - read-write
- ondblclick Fires when the user depresses then releases a mouse button twice - in quick succession over the input - inputObject.ondblclick = functionName; - Old Mac browsers (as well as a few others) generally do not detect this event - It is possible to emulate this event's response using a combination - of onclick and window.setTimeout - - read-write
- onfocus Fires when the input receives focus - inputObject.onfocus = functionName; - - read-write
- onkeydown Fires when the user presses a key down while the input has the focus - inputObject.onkeydown = functionName; - Some versions of Netscape 4 do not react but they will react to - document.onkeydown while the input has the focus - - read-write - Returning false will prevent the default keydown/press action in most browsers
- onkeypress Fires when the user presses and releases a key while the input has the focus - inputObject.onkeypress = functionName; - Some versions of Netscape 4 do not react but they will react to - document.onkeypress while the input has the focus - - read-write - Returning false will prevent the default keypress action in most browsers
- onkeyup Fires when the user releases a key while the input has the focus - inputObject.onkeyup = functionName; - Some versions of Netscape 4 do not react but they will react to - document.onkeyup while the input has the focus - - read-write
- onmousedown Fires when the user depresses a mouse button over the input - inputObject.onmousedown = functionName; - - read-write - Returning false from a right button mousedown will prevent the right click - menu from appearing in some browsers - Some may need you to return false from an oncontextmenu event and some - do not detect right clicks
- onmouseup Fires when the user releases a mouse button over the input - inputObject.onmouseup = functionName; - - read-write
- blur() Causes the input to loose focus
- focus() Causes the input to receive focus
- select() Causes the text in a text-based input to be selected
- target Refers to the target attribute of a form - - read-write
- elements[int numberOfInput] Refers to a non-image input - - read-only - - formObject.elements.length gives the number of inputs - - read-only
- onreset Fires when the form is reset - If you return false, the reset will be cancelled and the form will not be reset - inputObject.onreset = functionName; - - read-write
- onsubmit Fires when the form is submitted - If you return false, the submit will be cancelled and the form will not be submitted - inputObject.onreset = functionName; - - read-write
- reset() Causes the form to reset
- submit() Causes the form to be submitted
- nameOfImage Refers to an image - - read-only - - create a new image (stored in cache) using - imageObject = new Image(); imageObject.src = string src;
- border Refers to the border of an image - - read-write
- complete Boolean: says if an image has completed loading - - read-only
- height Refers to the height of an image - - read-write
- lowsrc Refers to the lowsrc of an image - - read-write
- name Refers to the name of an image - - read-write (read-only in some old browsers)
- src Refers to the src of an image - - read-write
- width Refers to the width of an image - - read-write
- onabort Fires if the user aborts the loading of that image - imageObject.onabort = functionName; - - read-write
- onerror Fires if the image fails to load due to errors in the image source or - if the image cannot be located - imageObject.onerror = functionName; - - read-write
- onload Fires as soon as the image loads - imageObject.onload = functionName; - Some browsers do not fire this event for cached images - - read-write
- onmousedown Fires when the user depresses a mouse button over the image - imageObject.onmousedown = functionName; - - read-write - - Returning false from a right button mousedown will prevent the right click - menu from appearing in some browsers - Some may need you to return false from an oncontextmenu event and some - do not detect right clicks
- onmouseup Fires when the user releases a mouse button over the image - imageObject.onmouseup = functionName; - - read-write
- referrer The URL of the page that contained a link that the user - clicked on to get to this page - representations may vary - Some browser allow users to disable this property - - read-only
- title Refers to the document title - - read-only in old browsers - - read-write in new browsers
- URL The full text of the location - as window.location.href except read-only - - read-only
- all[string IdOfElement] Refers to an element in a - proprietary DOM compatible browser - See Positioned Element - - read-only - - Proprietary DOM browsers only - For cross-browser scripting, see also - document.IdOfPositionedElement - document.layers - document.getElementById
- anchors[int numberOfATagWithNameDefined] Refers to an <a ...> tag where the name attribute has been set - - read-only - - documentObject.anchors.length gives the current number of anchor - elements whose name attribute has been set - - read-only
- name Refers to the name attribute of an anchor - - read-write
- offsetLeft The distance between the left edge of the anchor and the left edge of - the offsetParent node - See the section on browser specific referencing for how to use this - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - anchorObject.x
- offsetParent The offsetParent node - this may be different in different browsers - See the section on browser specific referencing for how to use this - - read-only - - Non-layers browsers only
- offsetTop The distance between the top edge of the anchor and the top edge of - the offsetParent node - See the section on browser specific referencing for how to use this - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - anchorObject.y
- x The distance between the left edge of the anchor and the left edge of the page - - read-only - - Layers browsers only - For cross-browser scripting, see also - linkObject.offsetLeft recursively with anchorObject.offsetParent
- y The distance between the top edge of the anchor and the top edge of the page - - read-only - - Layers browsers only - For cross-browser scripting, see also - linkObject.offsetTop recursively with anchorObject.offsetParent
- applets[int numberOfAppletTag] Refers to an applet in the document - - read-only
- nameOfPublicProperty All public properties of the applet are available - - read-only/read-write
- nameOfPublicMethod() All public methods of the applet are available - - read-only/read-write
- embeds[nameOrNumberOfEmbeddedObject] Refers to an embed element in the document - In many browsers, this array requires Java to be enabled - If the browser does not have Java enabled, the script - will produce an error and will abort if you attempt to - reference this array - If the plugin or the embedded object fails to load, Netscape 4 - will produce an error if you attempt to reference the - corresponding cell of the embeds array - - This array is empty in IE/Win and iCab 3- - - See also document.plugins - - read-only
- height Refers to the height of the embedded object - - read-only
- hidden Refers to the hidden attribute of the embedded object - - read-only
- name Refers to the name of the embedded object - - read-only
- pluginspage Refers to the pluginspage attribute of the embedded object - - read-write
- src Refers to the src of the embedded object - - read-write
- type Refers to the data type of the embedded object - - read-only
- units Refers to the units of the embedded object - (usu. 'pixels' in Netscape compatible or 'px' in - Internet Explorer compatible) - - read-only
- width Refers to the width of an the embedded object - - read-only
- onload Fires as soon as the embedded object loads - embeddedObject.onload = functionName; - - read-write
- nameOfPublicMethod() All public methods of the embedded object are available - These will vary with the plugin type - See the plugin provider's page - - read-only/read-write
- forms[int numberOfForm] Refers to a form in the document - Children as window.document.nameOfForm - - read-only
- images[nameOrNumberOfImage] Refers to an image in the document - Children as window.document.nameOfImage - - read-only
- layers[string IdOfPositionedElement] Refers to a positioned element in a - layers compatible browser - See Positioned Element - - read-only - - Layers browsers only - For cross-browser scripting, see also - document.IdOfPositionedElement - document.all - document.getElementById
- links[int numberOfATagWithHrefDefined] Refers to an <a ...> or <area ...> tag where the HREF attribute has been set - - read-only - - documentObject.links.length gives the current number of anchor or area - elements whose href attribute has been set - - read-only
- hash The part of the href after the # symbol (if there is one) - The hash symbol is also returned when reading this - The hash symbol should not be included when setting this - Internet Explorer (Win/Mac), Gecko and iCab 3- return a blank string if hash is just '#' - ICEbrowser returns the entire href without the hash - - read-write
- host The host name and port number (if there is one) of the href - - read-write
- hostname The host name of the href - - read-write
- href The full text of the href - - read-write
- innerText The visible text of the link - - read-only - - Proprietary DOM browsers only - For cross-browser scripting, see also - linkObject.text
- offsetLeft The distance between the left edge of the link and the left edge of - the offsetParent node - See the section on browser specific referencing for how to use this - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - linkObject.x
- offsetParent The offsetParent node - This may be different in different browsers - See the section on browser specific referencing for how to use this - - read-only
- offsetTop The distance between the top edge of the link and the top edge of - the offsetParent node - See the section on browser specific referencing for how to use this - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - linkObject.y
- pathname The part of the href after the host name - eg. subdir1/subdir2/file.html - - read-write
- port The port number (if there is one) - - read-write
- protocol The protocol used - eg. http: - - read-write
- search The part of the href after (and containing) the ? symbol, if there is one - - read-write
- target The target of the link - - read-write
- text The visible text of the link - - read-only - - A bug in Netscape 4 makes this only available if - the name attribute is not set - - Layers browsers only - For cross-browser scripting, see also - linkObject.innerText
- x The distance between the left edge of the link and the left edge of the page - - read-only - - A bug in Netscape 4 makes this only available if the name attribute is not set - If it is, use the x property of the corresponding anchor object - - Layers browsers only - For cross-browser scripting, see also - linkObject.offsetLeft recursively with linkObject.offsetParent
- y The distance between the top edge of the link and the top edge of the page - - read-only - - A bug in Netscape 4 makes this only available if the name attribute is not set - If it is, use the y property of the corresponding anchor object - - Layers browsers only - For cross-browser scripting, see also - linkObject.offsetTop recursively with linkObject.offsetParent
- onblur Fires when the link looses focus - anchorObject.onblur = functionName; - - read-write
- onclick Fires when the user depresses then releases a mouse button over the link - or if the link is activated using the keyboard (whereas mouse event - properties are not provided) - linkObject.onclick = functionName; - - read-write - - Returning false from a click will prevent the link from being followed
- ondblclick Fires when the user depresses then releases a mouse button twice - in quick succession over the link - linkObject.ondblclick = functionName; - Old Mac browsers (as well as a few others) generally do not detect this event - It is possible to emulate this event's response using a combination - of onclick and window.setTimeout - - read-write
- onfocus Fires when the link receives focus - anchorObject.onfocus = functionName; - - read-write
- onmousedown Fires when the user depresses a mouse button over the link - linkObject.onmousedown = functionName; - - read-write - - Returning false from a right button mousedown will prevent the right click - menu from appearing in some browsers - Some may need you to return false from an oncontextmenu event and some - do not detect right clicks
- onmousemove Fires when the user moves the mouse while it is over the link - linkObject.onmousemove = functionName; - Not available in Netscape 4 - - read-write
- onmouseout Fires when the user moves the mouse from over the link to off the link - linkObject.onmouseout = functionName; - - read-write
- onmouseover Fires when the user moves the mouse from off the link to over the link - linkObject.onmouseover = functionName; - - read-write - - If you set the status using this event, you will need to return true
- onmouseup Fires when the user releases a mouse button over the link - linkObject.onmouseup = functionName; - - read-write
- blur() Causes the link to loose focus - Not available in Netscape 4 - Check if the method exists before you try to use it - if( linkObject.blur ) { linkObject.blur(); }
- focus() Causes the link to receive focus - Not available in Netscape 4 - Check if the method exists before you try to use it - if( linkObject.focus ) { linkObject.focus(); }
- plugins[numberOfObject] Refers to an embed or object element in the document - (depending on what is supported by the browser) - In many browsers, this array requires Java to be enabled - If the browser does not have Java enabled, the script - will produce an error and will abort if you attempt to - reference this array - If the plugin or the embedded object fails to load, Netscape 4 - will produce an error if you attempt to reference the - corresponding cell of the embeds array - - This array is empty in IE/Win and iCab 3-, and does not - exist in older Konqueror/Safari/OmniWeb - In Opera it will reference all object and embed tags even if - they are not being used - In Mozilla/Firefox it will only contain objects or embeds - that have actually loaded plugins - - Children as for document.embeds - - read-only
- onclick Fires when the user depresses then releases a mouse button over the document - documentObject.onclick = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- ondblclick Fires when the user depresses then releases a mouse button twice - in quick succession over the document - documentObject.ondblclick = functionName; - Old Mac browsers (as well as a few others) generally do not detect this event - It is possible to emulate this event's response using a combination - of onclick and window.setTimeout - - Layers browsers will require you to use captureEvents first - - read-write
- onkeydown Fires when the user presses a key down while the document - (but not an input) has the focus - documentObject.onkeydown = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onkeypress Fires when the user presses and releases a key down while - the document (but not an input) has the focus - documentObject.onkeypress = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onkeyup Fires when the user releases a key while the document - (but not an input) has the focus - documentObject.onkeyup = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmousedown Fires when the user depresses a mouse button over the document - documentObject.onmousedown = functionName; - - Layers browsers will require you to use captureEvents first - - read-write - - Returning false from a right button mousedown will prevent the right click - menu from appearing in some browsers - Some may need you to return false from an oncontextmenu event and some - do not detect right clicks
- onmousemove Fires when the user moves the mouse while it is over the document - documentObject.onmousemove = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseout Fires when the user moves the mouse from over the document to off the document - documentObject.onmouseout = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseover Fires when the user moves the mouse from off the document to over the document - documentObject.onmouseover = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseup Fires when the user releases a mouse button over the document - documentObject.onmouseup = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- captureEvents(Event.EventType) Causes layers browsers to react to events - see window.Event for what events can be captured
- close() Says that you have stopped rewriting the document contents - and they should now be displayed
- getElementById(string IdOfElement) Returns a reference to an element in a - DOM compatible browser - See Positioned Element - - read-only - - DOM browsers only - For cross-browser scripting, see also - document.IdOfPositionedElement - document.layers - document.all
- open() Says that you are about to start rewriting the document contents
- write(string content) Writes document content
- writeln(string content) Writes a line of document content
- event If the browser supports it, this holds the instantaneous details of an event - See the section on event objects below - - read-only - - Proprietary DOM browsers only - For cross-browser scripting, see also - event objects using first argument
- Event The event capture object provided by layers browsers - For use with capturing events - Some browsers that support DOM events will provide this, - but it serves no useful purpose
- CLICK Fires when the user depresses then releases a mouse button over the element - - read-only
- DBLCLICK Fires when the user depresses then releases a mouse button twice - in quick succession over the element - Old Mac browsers (as well as a few others) generally do not detect this event - It is possible to emulate this event's response using a combination - of onclick and window.setTimeout - - read-write
- KEYDOWN Fires when the user presses a key down while the element has the focus - - read-only
- KEYPRESS Fires when the user presses then releases a key while the element has the focus - - read-only
- KEYUP Fires when the user releases a key while the element has the focus - - read-only
- MOUSEDOWN Fires when the user depresses a mouse button over the element - - read-only
- MOUSEMOVE Fires when the user moves the mouse while it is over the element - - read-only
- MOUSEOUT Fires when the user moves the mouse from over the element - to off the element - - read-only
- MOUSEOVER Fires when the user moves the mouse from off the element - to over the element - - read-only
- MOUSEUP Fires when the user releases a mouse button over the element - - read-only
- Function Only useful as a constructor - - read-only
- history Refers to the document history of the browser - - read-only
- back() Loads the last document in the browser history - Equivalent to clicking the browser's back button
- forward() Loads the next document in the browser history - Equivalent to clicking the browser's forward button
- go(int numerToJump) Jumps forward through the browser history by the specified number of entries - Use -ve numbers for previous documents
- Image Only useful as a constructor - - read-only
- innerHeight Represents the height of the document area of the browser in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-only - - Netscape compatible browsers only - For cross-browser scripting, see also - document.body.clientHeight - document.documentElement.clientHeight
- innerWidth Represents the width of the document area of the browser in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-only - - Netscape compatible browsers only - For cross-browser scripting, see also - document.body.clientWidth - document.documentElement.clientWidth
- length Number of frames held by the current document - Equivalent to window.frames.length - - read-only
- location Holds information about the current location - - read-write - - Writing is equivalent to writing location.href
- hash The part of the location after the # symbol (if there is one) - The hash symbol is also returned when reading this - The hash symbol should not be included when setting this - Internet Explorer (Win/Mac), Gecko and iCab 3- return a blank string if hash is just '#' - ICEbrowser returns the entire href without the hash - - read-write
- host The host name and port number (if there is one) of the location - Internet Explorer 7- on windows always shows a port number, even if one is not specified in the href (it does not do this with links) - - read-write
- hostname The host name of the location - - read-write
- href The full text of the location - - read-write
- pathname The part of the location after the host name - eg. /subdir1/subdir2/file.html - - read-write
- port The port number (if there is one) - - read-write
- protocol The protocol used - eg. http: - - read-write
- search The part of the location after (and containing) the ? symbol, if there is one - - read-write
- reload([bool forceFullReload]) Reloads the current document - if forceFullReload is true, the document will be reloaded from the - server, not from cache
- replace(string location) Loads the specified URL replacing the current history entry - the URL can be relative or absolute
- Math An object containing mathematical constants and methods - - read-only
- E Constant - e - ~= - 2.718 - - read-only
- LN2 Constant - ln(2) - ~= - 0.693 - - read-only
- LN10 Constant - ln(10) - ~= - 2.303 - - read-only
- LOG2E Constant - log2(e) - ~= - 1.442 - - read-only
- LOG10E Constant - log10(e) - ~= - 0.434 - - read-only
- PI Constant - Pi - ~= - 3.142 - - read-only
- SQRT1_2 Constant - 1/sqrt(2) or sqrt(1/2) - ~= - 0.707 - - read-only
- SQRT2 Constant - sqrt(2) - ~= - 1.414 - - read-only
- abs(number n) Returns the absolute value of n
- acos(number n) Returns (rads) cos^-1 of n
- asin(number n) Returns (rads) sin^-1 of n
- atan(number n) Returns (rads) tan^-1 of n
- atan2(number n,number k) Returns the angle (rads) from cartesian coordinates 0,0 to n,k
- ceil(number n) Returns n rounded up to the nearest whole number
- cos(number n) Returns cos n (where n is in radians)
- exp(number n) Returns e to the power of n
- floor(number n) Returns n rounded down to the nearest whole number
- log(number n) Returns ln(n)
- max(number a,number b,number c,.....) Returns the largest number
- min(number a,number b,number c,.....) Rreturns the smallest number
- pow(number n,number k) Rturns n to the power of k
- random() Returns a random number between 0 and 1
- round(number n) Returns n rounded up or down to the nearest whole number
- sin(number n) Returns sin n (where n is in radians)
- sqrt(number n) Returns the square root of n
- tan(number n) Returns tan n (where n is in radians)
- name Refers to the name of the current window used by <a target= ...> - - read-write
- navigator Holds information about the user's browser - - read-only
- appCodeName The code name of the browser - Many browsers spoof this, never use it - - read-only
- appName The short name of the browser - Many browsers spoof this, never use it - - read-only
- appVersion The version number of the browser - Many browsers spoof this, never use it - - read-only
- language The user's choice of language for their browser - - read-only - - Netscape compatible browsers only
- platform The operating system the browser is running on - Many browsers spoof this, never use it - - read-only
- userAgent The full name of the browser - Many browsers spoof this, never use it - - read-only
- userLanguage The user's choice of language for their operating system - - read-only - - IE compatible browsers only
- mimeTypes[nameOrNumberOfMIMEType] Contains information about a MIME type supported by the browser - Internet Explorer on Windows just gives a 0 length array so - Just put the object on the page and the user will be prompted to - download the relevant plugin if they do not support it yet - - read-only
- description The full description of the MIME type - - read-only
- enabledPlugin Refers to the plugin that supports the MIME type - children as for window.navigator.plugins[nameOrNumberOfPlugin] - - read-only
- suffixes Gives a 'comma-space' separated list of the file extensions covered by the MIME type - - read-only
- type Gives the official type name of the MIME type - eg. image/gif - - read-only
- plugins[nameOrNumberOfPlugin] Contains information about a plugin - Internet Explorer on Windows just gives a 0 length array so - Just put the object on the page and the user will be prompted to - download the plugin if they do not support it yet - - read-only
- description The full name of the plugin - - read-only
- filename The full name of the plugin file - - read-only
- length The number of MIME types supported by the plugin - - read-only
- name The short name of the plugin - - read-only
- nameOrNumberOfMIMEType A reference to the associated entry in the navigator.mimeTypes collection - - read-only
- javaEnabled() Returns if the browser supports Java
- taintEnabled() Returns if the browser supports data tainting - Tainting is not supported by any '4th generation' - browsers so this method no longer serves any - purpose, although browsers still provide it
- Number Contains unusual constants and information about the browser's number handling - Can also be used as a constructor - - read-only
- MAX_VALUE Number.MAX_VALUE is the highest number that that browser on - that operating system can represent - - read-only
- MIN_VALUE Number.MIN_VALUE is the smallest positive number that that browser - on that operating system can represent - - read-only
- NaN Number.NaN is a non-number equivalent to eg. Math.sqrt(-1) - - read-only
- NEGATIVE_INFINITY Number.NEGATIVE_INFINITY is a special number representing negative infinity - - read-only
- POSITIVE_INFINITY Number.POSITIVE_INFINITY is a special number representing positive infinity - - read-only
- Object Only useful as a constructor - - read-only
- opener Refers to the window that used the window.open method to open this window - - read-write
- Option Only useful as a constructor - - read-only
- outerHeight Represents the height of the browser window in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-only - - There is no cross-browser equivalent - It is best to use this only to combat resizeTo inconsistencies
- outerWidth Represents the width of the browser window in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-only - - There is no cross-browser equivalent - It is best to use this only to combat resizeTo inconsistencies
- pageXOffset Represents the horizontal scrolling of the document in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-write - - Netscape compatible browsers only - For cross-browser scripting, see also - document.body.scrollLeft - document.documentElement.scrollLeft
- pageYOffset Represents the vertical scrolling of the document in pixels - in most browsers (not IE 8- or IE 9+ in quirks mode) - - read-write - - Netscape compatible browsers only - For cross-browser scripting, see also - document.body.scrollTop - document.documentElement.scrollTop
- parent Refers to the parent window in the frameset - - read-only
- RegExp Holds information about regular expression searches - Some browsers fail to implement this correctly (eg. Opera 6- and iCab 2-) - - read-only
- $1 ... $9 RegExp.$1 - RegExp.$9 store the last 9 capture matches found - during a regular expression search - - read-only
- input RegExp.input is the last string that a regular expression - search was performed against - - read-only
- lastMatch or $& RegExp.lastMatch is the last substring that fully matched a - regular expression during a match, exec or test - - read-only
- lastParen or $+ RegExp.lastParen is the last instance of any capture during - a match, exec or test - - read-only
- leftContext or $` RegExp.leftContext is the leading substring before the last - substring that fully matched a regular expression during a - match, exec or test - - read-only
- rightContext or $' RegExp.rightContext is the trailing substring after the last - substring that fully matched a regular expression during a - match, exec or test - - read-only
- screen Holds information about the user's monitor - - read-only
- availHeight The available height of the monitor in pixels when - The size of the taskbar is taken into account - - read-only
- availWidth The available width of the monitor in pixels when - The size of the taskbar is taken into account - - read-only
- colorDepth The number of bits per pixel if pixelDepth - is not provided - - read-only - - For cross-browser scripting, see also - pixelDepth
- height The height of the monitor in pixels - - read-only
- pixelDepth The number of bits per pixel, if provided - - read-only - - For cross-browser scripting, see also - colorDepth
- width The width of the monitor in pixels - - read-only
- status Refers to the text written in the status bar of the browser - Some browsers allow users to disable the ability to write to this toolbar - - read-write
- String Mostly useful as a constructor - Contains a useful character method - - read-only
- fromCharCode(number asciiCharacterValue) Returns the character represented by the numerical (ascii) value - - read-only
- frames[nameOrNumberOfFrame] Refers to an iframe element or the window object of a page held - in the frameset held by this page (you will need to use - window.frames[nameOrNumberOfFrame].window - to access the window object of the document held in an iframe) - - read-only - windowObject.frames.length gives the number of frames in the - frameset held by this page - NOTE: attempting to access a frame that does not contain an - HTML document or is from a different domain or an iframe - that has not loaded will cause errors in most browsers - - read-only
- name Refers to the name of a frame - - read-write
- onblur Fires when the window looses focus - windowObject.onblur = functionName; - - read-write
- onerror Fires when the script produces an error. - Setting this prevents the error dialog being displayed - windowObject.onerror = functionName; - or - windowObject.onerror = null; - Returning true will prevent the error being logged - - Not available in some browsers (like Opera), so use try{}catch(e){} instead - - read-write
- onfocus Fires when the window receives focus - windowObject.onfocus = functionName; - - read-write
- onload Fires as soon as the page loads - windowObject.onload = functionName; - IE 4 will get self.onload wrong (use window.onload) - - read-write
- onunload Fires just before the page unloads - windowObject.onunload = functionName; - - read-write
- alert(object) Displays a message box with a textual representation of the object. - If the object is a string, the string will be displayed
- blur() Causes the window to loose focus
- clearInterval(interval object) Stops the interval from firing again
- clearTimeout(timeout object) Stops the timeout from firing
- close() Causes the window to close - If the window was not opened by script, the user will - be asked if they want the window to close
- confirm(string message) Displays the message and offers 'OK' and 'Cancel' buttons - Returns true if the user chooses OK and false if the user chooses Cancel
- escape(string textToURLEncode) Returns the string URL encoded - Not unicode-safe
- eval(string scriptToEvaluate) Evaluates the text as a script
- focus() Causes the window to receive focus
- isFinite(number numberToCheck) Returns if the number is a proper number with a finite value - not NaN or Infinity or -Infinity
- isNaN(number numberToCheck) Returns if the number is NaN (eg. Math.sqrt(-1))
- moveBy(int xOffset,int yOffset) Moves the browser window by the specified amount
- moveTo(int xPos,int yPos) Moves the browser window to the specified coordinate
- open(string url[,string targetName[,string options[,bool replaceHistoryEntry]]]) Opens the URL specified with the settings provided - Returns a reference to the window - The targetName is used by <a target= ...> - Options is a string containing a comma separated list of - option=value where value is an integer giving a size or - yes or 1 to enable the option or no or 0 to disable it - available options are: - height=int height of window - left=int distance between left side of window and left side of screen - location=enable/disable - menubar=enable/disable - resizable=enable/disable - scrollbars=enable/disable - status=enable/disable - toolbar=enable/disable - top=int distance between top of window and top of screen - width=int width of window - Some browsers may offer more - Leave any out to resort to their defaults
- parseInt(string textContainingAnInteger[, int radix]) Returns the integer part of a string - If radix is provided, the number will be interpreted in the base - of that radix (typically 8, 10 or 16) - If radix is not provided, older browsers will auto-detect leading - '0x' as Hex, leading '0' as octal, and all others as decimal.
- parseFloat(string textContainingAFloat) Returns the float part of a string
- print() Displays the print dialog box allowing the user to print the current document - Not available in some older browsers
- prompt(string message,string defaultValue) Displays a prompt allowing the user to input a variable value - Returns the user-inputted value as a string - Returns null or undefined if the user chooses cancel
- resizeBy(int xOffset,int yOffset) Resizes the browser window by the specified amount
- resizeTo(int xWidth,int yWidth) Resizes the browser window to the specified amount
- scrollBy(int xOffset,int yOffset) Scrolls the document by the specified amount
- scroll or scrollTo(int xPos,int yPos) Scrolls the document to the specified coordinate
- setInterval(string or function scriptToEvaluate,int timeInMilliseconds) Repeatedly waits for the specified amount of time - before evaluating the code specified - Returns an integer reference to the interval object
- setTimeout(string or function scriptToEvaluate,int timeInMilliseconds) Waits for the specified amount of time - before evaluating the code specified - Returns an integer reference to the timeout object
- unescape(string textToURLUnEncode) Returns the string URL unencoded - Not unicode-safe
Positioned elements
In DOM and proprietary DOM browsers, this could actually be any element, not just one that is positioned. However, at this stage of the tutorial, only 4th generation DHTML is being discussed, and therefore this is referred to as a positioned element. The next chapter will show how this applies to all elements, not just those that are positioned.
- PositionedElement A div (or span) element that has been absolutely positioned using stylesheets - Referred to using any of - document.getElementById - document.IdOfElement - document.all - document.layers - as above - - read-only
- bgColor Refers to the default background colour of the element - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.background - positionedElement.style.backgroundColor
- clip If available, this refers to the visible area of the element - - read-only - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.clip
- bottom The distance between the top edge of the element and the start - of the bottom portion of the element that is not visible - in pixels (integer) - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.clip
- left The size of the left portion of the element that is not visible - in pixels (integer) - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.clip
- right The distance between the left edge of the element and the start - of the right portion of the element that is not visible - in pixels (integer) - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.clip
- top The size of the top portion of the element that is not visible - in pixels (integer) - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.clip
- document In layers browsers, this refers to the contents of the element, equivalent to a whole new document - (see window.document for what properties will be available) - In many other browsers (such as Opera), this is a reference to window.document - - read-only
- id Refers to the id of the element - In layers browsers, if the name attribute is set, this will be the name attribute and the name attribute must be used instead when referring to the element - For this reason, it is best not to set the name attribute - - read-write
- innerHTML Refers to the contents of the element - as part of the main document - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.document.open, write, writeln and close
- left If available, this is an integer specifying the left coordinate of the element - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.left - positionedElement.style.pixelLeft (not required)
- name Refers to the name of the element - In layers browsers, if the name attribute is set, the positionedElement.id will be the name attribute and the name attribute must be used instead when referring to the element - For this reason, it is best not to set the name attribute - - read-only
- style If available, this refers to the style of the element - - read-only
- background Refers to the default background colour of the element - Browsers other than Opera 5.x may also give other information, eg. background images etc. - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.bgColor - positionedElement.style.backgroundColor
- backgroundColor Refers to the default background colour of the element - - read-write - - Non-layers browsers only (not available in Opera 5.x) - For cross-browser scripting, see also - positionedElement.bgColor - positionedElement.style.background
- clip Refers to the coordinates of the visible area of the element - in string format: - rect(topPos, rightPos, bottomPos, leftPos) - All values should be in pixels (for compatibility reasons) - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - children of positionedElement.clip
- color Refers to the default colour of the text in the element - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.document.fgColor
- height Refers to the height of the element - In the format '100px' - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.resizeTo - positionedElement.pixelHeight
- left Refers to the left coordinate of the element - In the format '100px' - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.left - positionedElement.style.pixelLeft (not required)
- pixelHeight Refers to the height of the element - In pixels as an integer - - read-write - - Internet Explorer compatible only - For cross-browser scripting, see also - positionedElement.resizeTo - positionedElement.style.height
- pixelLeft Refers to the left coordinate of the element - In pixels as an integer - - read-write - - Internet Explorer compatible only - For cross-browser scripting, see also - positionedElement.left - positionedElement.style.left
- pixelTop Refers to the top coordinate of the element - In pixels as an integer - - read-write - - Internet Explorer compatible only - For cross-browser scripting, see also - positionedElement.top - positionedElement.style.top
- pixelWidth Refers to the width of the element - In pixels as an integer - - read-write - - Internet Explorer compatible only - For cross-browser scripting, see also - positionedElement.resizeTo - positionedElement.style.width
- top Refers to the top coordinate of the element - In the format '100px' - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.top - positionedElement.style.pixelTop (not required)
- visibility Refers to the visibility of the element - Available values are 'visible' and 'hidden' - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.visibility
- width Refers to the width of the element - In the format '100px' - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.resizeTo - positionedElement.pixelWidth
- zIndex Refers to the z-index (stacking order) of the element - Must be a positive integer - - read-write - - Non-layers browsers only - For cross-browser scripting, see also - positionedElement.zIndex
- top If available, this is an integer specifying the top coordinate of the element - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.top - positionedElement.style.pixelTop (not required)
- visibility Refers to the visibility of the element - Available values are 'show' and 'hide' - - read-write - - for cross-browser scripting, see also - positionedElement.style.visibility
- zIndex Refers to the z-index (stacking order) of the element - Must be a positive integer - - read-write - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.zIndex
- onclick Fires when the user depresses then releases a mouse button over the positioned element - positionedElement.onclick = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- ondblclick Fires when the user depresses then releases a mouse button twice - in quick succession over the positioned element - positionedElement.ondblclick = functionName; - Old Mac browsers (as well as a few others) generally do not detect this event - It is possible to emulate this event's response using a combination - of onclick and window.setTimeout - - Layers browsers will require you to use captureEvents first - - read-write
- onkeydown Fires when the user presses a key down while the positioned element (but not an input) has the focus - positionedElement.onkeydown = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onkeypress Fires when the user presses and releases a key down while - the positioned element (but not an input) has the focus - positionedElement.onkeypress = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onkeyup Fires when the user releases a key while the positioned element - (but not an input) has the focus - positionedElement.onkeyup = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmousedown Fires when the user depresses a mouse button over the positioned element - positionedElement.onmousedown = functionName; - - Layers browsers will require you to use captureEvents first - - read-write - - Returning false from a right button mousedown will prevent the right click menu from appearing in some browsers - Some may need you to return false from an oncontextmenu event and some do not detect right clicks
- onmousemove Fires when the user moves the mouse while it is over the positioned element - positionedElement.onmousemove = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseout Fires when the user moves the mouse from over the positioned element to off the positioned element - positionedElement.onmouseout = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseover Fires when the user moves the mouse from off the positioned element to over the positioned element - positionedElement.onmouseover = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- onmouseup Fires when the user releases a mouse button over the positioned element - positionedElement.onmouseup = functionName; - - Layers browsers will require you to use captureEvents first - - read-write
- captureEvents(Event.EventType) Causes layers browsers to react to events - See window.Event for what events can be captured
- resizeTo(int width,int height) Resizes the layer by changing the values of clip.bottom and clip.right - - Layers browsers only - For cross-browser scripting, see also - positionedElement.style.width and positionedElement.style.height - positionedElement.style.pixelWidth and positionedElement.style.pixelHeight
Event objects
- window.event or first argument passed to handler function Holds records of mouse buttons or keys that triggered the event - - Available if triggered by: - object.onEventName = handlerFunctionName; - or - <element onEventName="thisIsEquivalentToAHandlerFunction;"> - - read-write
- altKey Boolean: says if the Alt key was pressed during the event - Note: some browsers do not provide this or the layers - compatible method of checking these keys - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.modifiers
- button If a left mouse button triggered the event, this will be 0 or 1 - If a right mouse button triggered the event, this will be 2 - Some browsers do not detect right-clicks - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.which
- clientX If the mouse triggered the event, this holds the horizontal position of the mouse - Position is relative to the displayed portion of the page - Three old browsers provide this is relative to the whole page - Current releases of these browsers support it correctly - See the chapter on 'Event information' for more details - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.pageX
- clientY If the mouse triggered the event, this holds the vertical position of the mouse - Position is relative to the displayed portion of the page - Three old browsers provide this is relative to the whole page - Current releases of these browsers support it correctly - See the chapter on 'Event information' for more details - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.pageY
- ctrlKey Boolean: says if the Ctrl key was pressed during the event - Note: some browsers do not provide this or the layers - compatible method of checking these keys - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.modifiers
- keyCode A number that represents the key pressed - Keypad codes may vary - - read-only - - Non-layers browsers only - For cross-browser scripting, see also - eventObject.which
- modifiers A number that represents the combination of special keys pressed during the event - 1 = Alt, 2 = Ctrl, 4 = Shift, 8 = Command (MAC) - Can be added together if combinations are pressed - Note: some browsers do not provide this or the alternative method - of checking these keys - - read-only - - Layers browsers only - For cross-browser scripting, see also - eventObject.altKey - eventObject.ctrlKey - eventObject.shiftKey
- pageX If the mouse triggered the event, this holds the horizontal position - of the mouse relative to the whole page - - read-only - - Netscape and DOM compatible browsers only - For cross-browser scripting, see also - eventObject.clientX
- pageY If the mouse triggered the event, this holds the vertical position - of the mouse relative to the whole page - - read-only - - Netscape and DOM compatible browsers only - For cross-browser scripting, see also - eventObject.clientY
- screenX If the mouse triggered the event, this holds the horizontal position - of the mouse relative to the screen - - read-only
- screenY If the mouse triggered the event, this holds the vertical position - of the mouse relative to the screen - - read-only
- shiftKey Boolean: says if the Shift key was pressed during the event - Note: some browsers do not provide this or the layers - compatible method of checking these keys - - read-only - - Non-layers compatible browsers only - For cross-browser scripting, see also - eventObject.modifiers
- target Refers to the object that triggered the event - irrespective of capturing or bubbling - - read-only - - Netscape and DOM compatible browsers only - For cross-browser scripting, see also - eventObject.srcElement
- this The word 'this' when written as part of the handler function code - refers to the object that triggered the event - With capturing or bubbling, 'this' will refer to each element in turn - Netscape and DOM compatible browsers only - - read-only
- type A string giving the type of event that was triggered - this will be in lower case, without the 'on' - eg. mouseover - In KHTML 2 browsers, this will be prefixed with 'khtml_' for key events - - read-only
- srcElement Refers to the object that triggered the event - irrespective of capturing or bubbling - - read-only - - Internet Explorer compatible browsers only - For cross-browser scripting, see also - eventObject.target
- which If a left mouse button triggered the event, this will be 1 - If a right mouse button triggered the event, this will be 3 - Some browsers do not detect right-clicks - - If a key triggered the event, this will be the key code (keypad codes may vary) - - read-only - - Netscape compatible browsers only - For cross-browser scripting, see also - eventObject.keyCode - eventObject.button
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] A variable containing any number of variables within it - Reverses and sorts work only on numerically indexed arrays - - read-write- length The number of cells in a numerically indexed array - - read-write
- concat(elementToAdd[,elementToAdd[,elementToAdd[,etc.]]]) Adds cells to the end of the array containing the specified values - If elementToAdd is an array, its cells are concatenated separately as cells of the array - Returns the new array with the cells added to it
- join([string separatorToReplaceComma]) Returns a string representation of the array with the cell - contents separated by the specified string (default is ',')
- pop() Returns the contents of the last cell from the array - The array is then shortened so that it no longer contains that cell - - Not supported in IE 5.2- Win/Mac
- push(elementToAppend) Adds elementToAppend to the end of the array - - Not supported in IE 5.2- Win/Mac - - It is more efficient and compatible to use: - Array[Array.length] = elementToAppend
- reverse() Reverses the order of the cells in the array
- shift() Returns the contents of the first cell from the array - The array is then shortened so that it no longer contains that cell - The indexes of all other cells are decremented accordingly - - Not supported in IE 5.2- Win/Mac
- slice(int offsetFromStart[,int offsetToEnd]) Returns an array with the cells taken away from the specified portion of the array - If indexToEnd is negative, it is taken as an offset from the end - If it is omitted, then it is assumed to be Array.length
- sort([function sortFunction]) Sorts the array in order of the values of its cells unless sortFunction is specified - sortFunction must accept two arguments - If the function returns > 0, the second argument will be sorted higher than the first - If the function returns < 0, the second argument will be sorted lower than the first - If the function returns 0, the entries will remain as they are with respect to each other
- splice(int offsetFromStart,int numberToRemove[,elementToAdd[,elementToAdd[,etc.]]]) Removes the specified number of cells from the array, - starting at the specified offset - Optionally adds the new elements as cells in the position where - the earlier cells were removed from - The number of cells added does not have to match the number of cells removed - (but it is generally more efficient if they are the same) - Returns an array containing the cells that were removed - - Not supported in IE 5.2- Win/Mac
- toString() or valueOf() Returns a string representation of the array
- unshift(elementToAppend) Adds elementToAppend to the start of the array, and - increments the indexes of all other cells accordingly - - Not supported in IE 5.2- Win/Mac
Boolean
- Boolean([bool value])
true or false A true or false value - - read-write- toString() Returns the word true or false
- valueOf() Returns the boolean value
Date
Note, a UNIX timestamp (milli) is the number of milliseconds since 00:00:00.000 01/01/1970.
Note also that UTC is the time as it would be in the GMT timezone, so GMT and UTC are equivalent.
- Date()
Date(int UNIXTimestampMilli)
Date(year, month, date[, hours[, minutes[, seconds[,ms]]]]) A variable containing a date in several representations - - read-write- getDate() Returns the day of the month represented by the date object
- getDay() Returns the day of the week represented by the date object
- getFullYear() Returns the year represented by the date object in 4 digit format - Not supported by old browsers - See getYear()
- getHours() Returns the hour represented by the date object in 24 hour format
- getMilliseconds() Returns the millisecond represented by the date object
- getMinutes() Returns the minute represented by the date object
- getMonth() Returns the month represented by the date object (0-11)
- getSeconds() Returns the second represented by the date object
- getTime() Returns the UNIX timestamp (in milliseconds) represented by the date object
- getTimezoneOffset() Returns the number of hours the user's computer is away from GMT
- getUTCDate() Returns the day of the month represented by the date object converted to GMT
- getUTCDay() Returns the day of the week represented by the date object converted to GMT
- getUTCFullYear() Returns the year represented by the date object in 4 digit format converted to GMT
- getUTCHours() Returns the hour represented by the date object in 24 hour format converted to GMT
- getUTCMilliseconds() Returns the millisecond represented by the date object converted to GMT
- getUTCMinutes() Returns the minute represented by the date object converted to GMT
- getUTCMonth() Returns the month represented by the date object converted to GMT
- getUTCSeconds() Returns the second represented by the date object converted to GMT
- getYear() Returns the year represented by the date object - This could be in a variety of formats depending on the browser - As JavaScript does not support dates before 1970 or beyond 2038 anyway, the following algorithm solves all problems - - theYear = dateObject.getYear() % 100; //0-38 or 70-99 - theYear += ( theYear < 39 ) ? 2000 : 1900; //1970-2038 - - or - - theYear = ( dateObject.getYear() % 100 ) + ( ( ( dateObject.getYear() % 100 ) < 39 ) ? 2000 : 1900 );
- setDate(int dayOfMonth) Sets the day of the month represented by the date object (0-11)
- setFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]]) Sets the year represented by the date object in 4 digit format
- setHours(int hours[,int minutes[,int seconds[,int milliseconds]]]) Sets the hour represented by the date object in 24 hour format
- setMilliseconds(int milliseconds) Sets the millisecond represented by the date object
- setMinutes(int minutes[,int seconds[,int milliseconds]]) Sets the minute represented by the date object
- setMonth(int month[,int dayOfMonth]) Sets the month represented by the date object
- setSeconds(int seconds[,int milliseconds]) Sets the second represented by the date object
- setTime(int UNITXTimestampMilli) ets the UNIX timestamp (in milliseconds) represented by the date object
- setUTCDate(int UNITXTimestampMilli) Sets the day of the month represented by the date object converted to GMT
- setUTCFullYear(int yearIn4DigitFormat[,int month[,int dayOfMonth]]) Sets the year represented by the date object in 4 digit format converted to GMT
- setUTCHours(int hours[,int minutes[,int seconds[,int milliseconds]]]) Sets the hour represented by the date object in 24 hour format converted to GMT
- setUTCMilliseconds(int milliseconds) Sets the millisecond represented by the date object converted to GMT
- setUTCMinutes(int minutes[,int seconds[,int milliseconds]]) Sets the minute represented by the date object converted to GMT
- setUTCMonth(int month[,int dayOfMonth]) Sets the month represented by the date object converted to GMT
- setUTCSeconds(int seconds[,int milliseconds]) Sets the second represented by the date object converted to GMT
- setYear(int numberOfYearsSince1900) Sets the difference between 1900 and the year represented by the date object
- toUTCString or toGMTString() Returns the date as a string, converted to GMT
- toString or toLocaleString() Returns the date as a string
- valueOf() Returns the UNIX timestamp (in milliseconds) represented by the date object
Function
- Function([string varName,[string varName2,[etc.]]]string scriptToEvaluate)
function functionName(listOfVariables) { function code }
functionName = function (listOfVariables) { function code } A series of instructions that can be activated as and when necessary - Can return values - - read-write- caller Refers to the function that called this function (or null if none) - Note, it is a property of the individual function and must be written as - functionObject.caller - - Warning, this property is non-standard and is not available in many browsers, including - Opera, Safari 2-, Konqueror 3.5.6-, NetFront, ICEbrowser, Escape and Clue - - Do not use this property - - read-write but unusable if written to
- prototype If the function is used as an object constructor; - functionObject.prototype.newPropertyName - creates new properties for all objects of that class - - read-write but unusable if written to
- this 'this' written as part of the function code refers to the current object - If activated directly by an event, this will be the object that triggered the event - If being used to construct an object, this will refer to the construct object - (that is the instantaneous object being constructed, which is stored temporarily until it is returned) - If neither of these are satisfied, this refers to window - 'this' is an instantaneous variable, and is not a property of the function - - read-only
- arguments[] The arguments collection when written as part of the function code - refers to the collection of variables passed to the function - Note, it is a property of the individual function but can be written as - either functionObject.arguments or just arguments - - read-write but unusable if written to
- callee A reference to the function that this code is running inside - Not supported by WebTV/MSNTV - - read-write but unusable if written to
- apply(object: thisObject[,array: arguments]) Runs the function as if it were a method of the thisObject - passing it the arguments from the arguments parameter - - Not supported in IE 5.2- Win/Mac, WebTV/MSNTV, or early Netscape 4 versions
- call(object: thisObject[,argument[,argument[,etc.]]]) Runs the function as if it were a method of the thisObject - passing it the separate arguments from the subsequent parameters - - Not supported in IE 5.2- Win/Mac, WebTV/MSNTV, or early Netscape 4 versions
- toString() Returns a string representation of the function - - In theory, for user-defined functions, the string should - be valid function code (with anonymous function braces) - However, most browsers will have problems with certain - constructs, and browsers on devices with limited memory - may not provide anything at all
- valueOf() Returns the function
Image
- Image([int width,int height]) Creates an image that is not displayed but is stored in cache - Children as for window.document.nameOfImage - The image will not be loaded until the src is set - - read-write
Number
- Number(numberOrString value)
number in hex, octal or decimal A variable with a numerical value - Can be negative or positive or NaN or Infinity and can have E+ or E- - - read-write- toFixed(precision) Returns the number as a string, rounded and formatted - with the given number of decimal places - Not supported in most old browsers, including IE 5.0- - Most non-IE browsers return large numbers in scientific - notation
- toPrecision(precision) Returns the number as a string, reduced to a precision of the - given number of decimal places - May be returned in scientific notation - Not supported in most old browsers, including IE 5.0-
- toExponential(precision) Returns the number as a string, rounded and formatted - in scientific notation with the given number of digits after - the decimal point - Not supported in most old browsers, including IE 5.0-
- toLocaleString() Returns the number as a string, - formatted according to the user's locale - Not supported in most old browsers
- toString() Returns the number as a string
- valueOf() Returns the number
Object
- Object()
{ propertyName1: value1[, propertyName2: value2[, etc.]] } Creates a variable that can have properties or child objects - - read-write- constructor Returns a reference to the function that is used as a constructor for this class of object
- hasOwnProperty(string propertyName) Returns true if the given property is attached directly to the object instance
- toString() Returns a string representation of the object - (typically [object Object])
- valueOf() Returns a string representation of the object - (typically [object Object])
Option
- Option([string text,string value[,bool selected]]) Creates an select input option that can be stored until it is inserted into a select input using: - window.document.nameOfForm.nameOfSelect.options[int optionNum] = optionObject - Children as for window.document.nameOfForm.nameOfSelect.options[int optionNum] - - read-write
Regular Expression
- RegExp(string pattern,string options)
/pattern/options A text pattern matching series of meta and normal characters - Some old browsers fail to implement this correctly (eg. Opera 6- and iCab 2-) - - read-write- compile(string pattern,string options) Redefines the regular expression and compiles it for faster use than normal - Only useful if using the same regular expression multiple times - Redundant in current browsers; do not use it
- exec(string stringToMatch) Attempts to match the regular expression against the string - Returns an array of the details of the last match or null if none: - - [ - last substring that fully matched the regular expression, - last instance of first capture, - ..., - last instance of ninth capture - ] - - Returned array has two other properties: - index - the position within the string where the pattern began to match (read-only) - input or $_ - the original string (read-only)
- global Boolean: says if the 'g' option was used for a global match - - read-only
- ignoreCase Boolean: says if the 'i' option was used to ignore case - - read-only
- lastIndex The index where the last match occurred during exec or test (read) - OR - The index at which to start the next match (write)
- source The text of the pattern - - read-only
- test(string stringToMatch) Attempts to match the regular expression against the string - Returns boolean if a match is found
String
- String([stringOrObjectToBeRepresentedAsString])
'content'
"content" A variable consisting only of characters - These can come from any character set supported by the current document - - read-write- length The number of characters in the string - - read-write
- anchor(string nameOfAnchor) Surrounds the string with <a name="nameOfAnchor"> tags - - For use when dynamically generating page content - Use ID attributes instead
- big() Surrounds the string with <big> tags - - For use when dynamically generating page content - There is almost always a more appropriate element you can use
- blink() Surrounds the string with <blink> tags - - For use when dynamically generating page content - This element is non-standard (and annoying), do not use it
- bold() Surrounds the string with <b> tags - - For use when dynamically generating page content - You may want to use <strong> instead
- charAt(int index) Returns the character at the specified index
- charCodeAt(int index) Returns the numerical value of the character at the specified index
- concat(string stringToAppend[,string stringToAppend[,string stringToAppend etc.]]) Returns the strings appended to each other and the string object in order
- fixed() Surrounds the string with <tt> tags - - For use when dynamically generating page content - There is almost always a more appropriate element you can use - (<code>, <samp> or <var>)
- fontcolor(string colorValue) Surrounds the string with <font color="colorValue"> tags - - For use when dynamically generating page content - This element is deprecated, do not use it
- fontsize(int size) Surrounds the string with <font size="size"> tags - - For use when dynamically generating page content - This element is deprecated, do not use it
- indexOf(string searchString[,int offset]) Returns the first position of the searchString within the string object - Returns -1 if none
- italics() Surrounds the string with <i> tags - - For use when dynamically generating page content - You may want to use <em> instead
- lastIndexOf(string searchString[,int offset]) Returns the last position of the searchString within the string object - Returns -1 if none
- link(string hrefToLinkTo) Surrounds the string with <a href="hrefToLinkTo"> tags - - For use when dynamically generating page content
- match(RegExp searchExpression) If the regular expression does not have the global flag, it - returns an array containing the part of the string that matched - the expression, followed by every capture from the regular - expression in sequence, from the first match within the string - - If the regular expression does have the global flag, it returns - an array containing every match of the entire regular - expression against the string - - If there are no matches, it returns null - - The first 9 captures/matches of the regular expression are - also stored in the global RegExp object
- replace(RegExp searchExpression,string replacementText) Replaces the first occurrence of the specified regular - expression within the string with the replacement text
- search(RegExp searchExpression) Returns the first position of the regular expression within the string object - Returns -1 if none
- slice(int offsetFromStart[,int offsetFromEnd]) Returns the substring starting at the specified index and ending - at the specified offset from the end - If the offset is not specified, the offset is taken as 0
- small() Surrounds the string with <small> tags - - For use when dynamically generating page content - There is almost always a more appropriate element you can use
- split(RegExp separator[,int maxArrayLength]) Splits a string on the occurrence of a specified substring - Results are returned as an array - If a maximum length is specifed, the final cell will contain any amounts - not split after that point
- strike() Surrounds the string with <s> tags - - For use when dynamically generating page content - This element is deprecated, do not use it - Usually, the <del> element is what you want to use
- sub() Surrounds the string with <sub> tags - - For use when dynamically generating page content
- substr(int startIndex[,int length]) Returns the substring starting at the specified index and the specified length long - If no length is specified, the length is to the end of the string
- substring(int startIndex,int endIndex) Returns the substring starting at the specified index and ending at the specified index
- sup() Surrounds the string with <sup> tags - - For use when dynamically generating page content
- toLowerCase() Returns the string in lower case
- toUpperCase() Returns the string in upper case
- valueOf() Returns the string
W3C DOM introduction
This part of the tutorial steps beyond the limits of the older browsers, and will cover the much more advanced abilities of the current browsers.
Thanks to PP:K for linking to this page from his Digital Web article about the W3C DOM. He refers to this as a 'less technical introduction' to the W3C DOM. I am happy with that description, not only because the 'technical introduction' is a 239 page PDF of incredible detail, but because I don't see why the DOM should be that technical. It is designed to be intuitive and easy to use, so that is how I will teach it. Of course there are some incredibly advanced and complex things you can do with the DOM, but with web pages, there is almost never the need to do them. So, instead of going into 239 pages of information on how to convert the planet Earth into a DOM tree, I will give you a gentle introduction to the fundamentals of the DOM, and show you how to achieve the results you need, with the minimum of fuss.
The W3C DOM, commonly known as 'The DOM' or the 'DOM level 1(/2/3)', provides a more realistic and more versatile way of looking at the structure of HTML documents (as well as XML and other document formats). It views HTML documents as a tree structure of elements and text embedded within other elements. All HTML elements, as well as the text they contain and their attributes can be referenced by walking through the DOM tree, their contents can be modified or deleted, and new elements can be created for subsequent insertion into the DOM tree. HTML elements, the text they contain, and their attributes are all known as nodes.
The DOM is designed to interact with more programming languages than just JavaScript, but as this is a JavaScript tutorial (in case you didn't notice...), and most browsers only support JavaScript, I will only describe how to use JavaScript to interact with it.
The DOM is the present and future of browsers, and no doubt it will become the only supported technique for both basic and advanced DHTML in the future.
The W3C DOM is supported by 5th generation browsers. These currently include:
- Opera 7+
- Mozilla Gecko (Mozilla, Firefox, Netscape 6+)
- KHTML/WebKit (Konqueror 2+, Safari, Chrome, 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. These browsers are no longer in common use, and can be safely ignored. You may want to check that a browser supports the W3C DOM before you attempt to use it. Note that Opera 4-6 also supports many of the basic functions of the W3C DOM, but cannot actually use most of them. iCab 2- cannot perform advanced W3C DOM functions. Therefore, the best test for W3C DOM support that I can find is this:
if( document.getElementById && document.childNodes && document.createElement ) {
//do something that requires it
}
Of all the current major browsers, Internet Explorer's DOM support is by far the worst of all. IE 8- for Windows supports half of the DOM 1 modules, and parts of a few DOM 2 modules. The Mac version supports slightly more of DOM 1. IE 9+ supports most of DOM 1 and 2, and a few basic DOM 3 modules, but only if the document triggers standards rendering mode. In quirks mode, IE 9+ reverts to its crippled old version of DOM, and since many scripts will need to work in both modes, they will need to allow for this, and restrict themselves to using only the parts IE supports, or has alternatives for.
Opera, Mozilla/Firefox and Safari/Chrome are competing for first place, with complete DOM 1 support, complete or almost complete DOM 2 support, and a small amount of DOM 3 support. iCab 3 has partial DOM 1 and DOM 2. Opera 9+ is the first and currently only browser to support all DOM 1 and 2 features well enough to claim support for all of them (as well as a number of DOM 3 modules). Safari/Chrome also claims support for all DOM 1 and 2 features, but its DOM 2 Style Sheets and CSS implementation is so bad it should not be allowed to claim support for them at all.
This tutorial will concentrate on things that will work in all these browsers, since they are the most common browsers currently in use, and will show how to bridge the gap to allow Internet Explorer's non-standard features to be used as a partial replacement for missing parts of important DOM features.
ICEbrowser has largely complete DOM 1 and partial DOM 2 (it also claims some DOM 3, but my tests cannot support its claims, especially since it is missing large parts of DOM 2 that it claims to support). It will cope with most of the things I will cover in this tutorial.
Escape 5 can handle enough DOM to reference elements and their styles, and can move them through the document. However, as soon as you try anything more than that (walking the DOM tree, creating new elements, changing their contents, checking for the existence of a childNodes collection, etc.), it produces script errors, and fails to use try...catch correctly, so the errors cannot be avoided. In this state, it is unusable, and it can't even handle many basic DHTML scripts either. Just ignore it. It is hardly used by anyone, and has been abandoned by the vendor.
NetFront has recently added DOM support and it is still very immature. Generally versions earlier than 3.4 will produce errors or crash, and as a result, should be ignored as DOM browsers. Version 3.4 usually performs better.
PP:K maintains many DOM 1 tutorials as well as the ever helpful DOM compatibility tables. Although the DOM provides many more methods, properties and collections than I have shown here, most are not reliable enough to use, so I will only talk you through those that are. If you intend to use other properties or methods, you should check if the browser supports them first using:
if( node.propertyCollectionOrMethodName ) {
//do something that requires it
}
DOM nodes and tree
The DOM tree
The following text is a snippet of HTML taken from a regular HTML document.
<p title="The test paragraph">This is a sample of some <b>HTML you might<br>have</b> in your document</p>
In your browser, this renders as this (hold your mouse over the paragraph to see the title - most browsers display it as a tooltip, some display it in the status bar):
This is a sample of some HTML you might
have in your document
The DOM tree views this (simplified) as follows:
P _______________|______________ | | childNodes attributes ______________|___________ | | | | title = 'The test paragraph' 'This is a sample of some ' B ' in your document' | childNodes __________|_______ | | | 'HTML you might' BR 'have'
Of course, the tree also extends above the 'P' from window.document, through the HTML element, down through the body element, then through any other container elements to the paragraph.
The parts of the DOM tree are known as nodes. The 'P', 'B' and 'BR' nodes are element nodes, childNodes and attributes are collections,
the title='The test paragraph'
pair is an attribute node, and the text strings are text nodes.
Referencing the element nodes
- 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's childNodes 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 use getElementsByTagName to reference all paragraphs, then choose the one we want. If the paragraph were to be given an id, we could also use getElementById:
document.getElementById('id of paragraph')
document.getElementsByTagName('p')[indexOfParagraph]
If we assigned the paragraph an id so that we could use getElementById, the id='elementID'
pair
would appear in the attributes collection, along side
title='The test paragraph'
in the tree diagram above. Note that if the document is served with an XML based
content-type header, getElementsByTagName becomes case sensitive.
NOTE: getElementsByTagName does not return a true collection, it returns an object with element index and 'length' properties. This object keeps itself up to date, so if an element it references is deleted or added, it will automatically change its item references to reflect that change.
We could even walk the entire DOM tree from the document object, for example:
window.document.childNodes[0].childNodes[1].childNodes[4]
In this case, window.document.childNodes[0] should be the HTML element, as it is the first tag in the document (assuming there is no doctype tag), and window.document.childNodes[0].childNodes[1] should be the body tag, as the head element will be the first child of the HTML element. Alternatively, there is a shortcut to the HTML element: document.documentElement so we could use this:
window.document.documentElement.childNodes[1].childNodes[4]
There is also a shortcut to the BODY element: document.body so we could use this:
window.document.body.childNodes[4]
Those last three examples are based on a simple page structure, where the paragraph is a direct child of the body element. Neither of these would be correct in the current document as the document structure is far more complex, also using DIV elements as parents of the paragraph.
The techniques used in those examples can be unreliable. Most browsers will correctly view the blank space between tags as a text node containing only white space characters (such as space, line-break or tab), even if the blank space is not rendered, such as a gap in between a <tr> tag and a <td> tag or a blank gap in between <p> tags. However, some browsers (mainly Internet Explorer 8-, and 9+ in quirks mode) will not view this empty space as a text node at all.
This means that the childNodes collection will be different lengths in these different browsers. If you are trying to walk the DOM tree to the next element node, for example, it may be worth checking each entry in the childNodes collection to see if its nodeType is 1, or to use node.getElementsByTagName.
Because of this, and the fact that the structure of the DOM tree is designed to change as elements are moved, added or removed, the only reliable way to reference an element is using its ID:
var theParagraph = document.getElementById('id of element')
The first entry of the childNodes collection can be accessed using the shortcut firstChild, and the last can be accessed using lastChild. node.nextSibling references the next entry in the parent node's childNodes collection and node.previousSibling references the previous one. To reference the parent node, we use node.parentNode. Note also that all element nodes have the getElementsByTagName method to help reference elements within them. This means that from any node, it is possible to reference any of the other notes around it.
Referencing the attribute node
- 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 use getAttribute.
window.alert( 'The value of the \'title\' attribute is: ' +
theParagraph.getAttribute('title') );
Attribute names are case sensitive. For example, bgcolor must be written as bgColor.
Test it here: get the attribute value.
Note that according to the specification, getAttribute should always return a string. However, this makes it impossible to differentiate between empty attributes and unspecified attributes. For this reason, browsers will return null for unspecified attributes, even though this is wrong. Opera 7-8 returns an empty string - this was changed to null in Opera 9. As a result, code that checks for attributes and incorrectly tests against null will fail in Opera 7 and 8, because a string will never equate to null. There is no need to test against null, just check if get attribute failed to retrieve a value:
if(!element.getAttribute('attribute_name'))
Changing the attribute
- Internet Explorer 7- (and some minor browsers) cannot set values for style, class or event handlers.
- Internet Explorer 8, and 9+ in quirks mode cannot set values for 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, using setAttribute. Internet Explorer 8 has fixed most of these, but still cannot set event handlers. Internet Explorer 9 can now set these attributes in standards mode. A few more browsers also have trouble reading these attributes using getAttribute. Internet Explorer generally returns the wrong type of result, such as an object instead of a string, when using getAttribute for these attributes. The DOM does provide a few alternatives to allow the functionality to be approximated in these browsers.
The class is available as a read/write string called className - this is discussed in the DOM CSS chapter of this tutorial.
The event handler attributes are available as referenced functions (this is not the case for handlers added using DOM events), with their names matching the attribute name; element.onclick. These are read/write but must be written as a reference to a function, not as a direct string. They can also be written as a string using the Function constructor:
element.onclick = new Function(codeAsAString);
They may also be read as a string using the toString method of the function, but note that it will normally contain the anonymous function wrapper, and may not be available at all in browsers running on devices with limited capabilities, such as mobile phones. Note also that it will not be available at all if the attribute is not present:
var functioncode = element.onclick.toString();
The string value of the style attribute is available as a read/write string called cssText, which
is a property of the style object, which itself is a property of the element. Note, however, that it is not
supported very well; Safari does not support it up to version 1.1 (reading it produced the value null
),
Mozilla versions prior to 1.0 could not write to it, and iCab 3-, NetFront and Escape/Evo do not support it at all.
To avoid problems with its use, a combination of cssText and getAttribute/setAttribute
can be used. To read it:
var cssString;
cssString = element.style.cssText;
if( typeof(cssString) != 'string' ) {
cssString = element.getAttribute('style');
}
To write it, simply set both versions, and the browser will use whichever one works:
var cssString = 'color:lime;font-weight:bold;';
element.style.cssText = cssString;
element.setAttribute('style',cssString);
Note that this will then prevent it from being read correctly if other styles are changed individually. If this will cause a problem, check if cssText is supported first:
var cssString = 'color:lime;font-weight:bold;';
if( typeof(element.style.cssText) == 'string' ) {
element.style.cssText = cssString;
}
element.setAttribute('style',cssString);
Referencing the text nodes
- 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 the firstChild (equivalent to the first entry in the childNodes collection) of the 'B' to reference the text node.
window.alert( 'The value of the text node is:\n' +
theParagraph.childNodes[1].firstChild.nodeValue );
Test it here: get the value of the text node.
Also important to note is that although the specifications say that no matter how much text exists between tags, it should all be in one text node, in practice this is not always the case. In Opera 7-9.2x and Mozilla/Netscape 6+, if the text is larger than a specific maximum size, it is split into multiple text nodes. These text nodes will be next to each other in the childNodes collection of the parent element.
In Opera 7-9.2x, this maximum text node size is 32 KB. In Mozilla/Firefox/Netscape 6+, it is 4 KB. Although the normalize() method of the parent node(s) should be able to replace the multiple text nodes with a single text node containing all the text, this only works in Mozilla/Firefox/Netscape 6+. In Opera 7-9.2x it puts all of the text into a single node and then truncates that node to 32 KB, so the contents of all except the first node are lost. Running the normalize method can crash Internet Explorer 6 and does not exist in Internet Explorer 5 on Windows.
For this reason, I do not recommend trying to normalize. It is better to manipulate the contents of text nodes separately. In fact, you can create your own text nodes and add them to the childNodes collection. Although to the DOM, they will still appear as separate nodes, they will appear as a single piece of text in the document. Basically, you need to be aware that your text may be split into several nodes, if it is 4 KB or over, or if you have added extra text nodes in yourself. In order to get that text in a single variable, you may need to look through every child node, and if they are consecutive text nodes append them together to get the total string.
Changing the text of text nodes
- 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 uses removeChild to delete the paragraph:
theNewParagraph.onclick = function () { this.parentNode.removeChild(this); };
Note that nodes belong to the document they were created in. So for example, if your page uses frames, and you create a paragraph node in one frame then attempt to add it to the document in another frame, it will not work. In theory you can use the document.importNode method to create a copy of it in the new document, but that method does not exist in Internet Explorer. If a script in one frame needs to create a node and append it to a document in another frame, it must use the document object for the destination frame when creating the node:
var newP = parent.frames['leftfr'].document.createElement('p');
parent.frames['leftfr'].document.body.appendChild(newP);
Using document fragments
- 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.
DOM tables
- 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. Most of those designed to create parts of tables, as well as the one to delete table captions, are not implemented properly in some older browsers, so I will concentrate on those that are the most reliable.
- table
- caption
- childNodes[]
- tHead
- rows[]
- cells[]
- childNodes[]
- cells[]
- rows[]
- tFoot
- rows[]
- cells[]
- childNodes[]
- cells[]
- rows[]
- tBodies[]
- rows[]
- cells[]
- childNodes[]
- cells[]
- rows[]
- caption
As well as being able to walk through the DOM tree as before, you can also walk through the DOM table tree. Each table has four extra properties that reference the various childNodes:
- caption
- References the caption of the table
- thead
- References the thead of the table, if there is one
- tfoot
- References the tfoot of the table, if there is one
- tbodies
- A collection with one entry for each tbody (usually just table.tbodies[0])
Each thead, tbody and tfoot also has a rows collection with an entry for each row in that thead, tbody or tfoot. Each row has a cells collection containing every td or th cell in that row. Each cell then contains the usual DOM references to its contents.
Each table also has the createTHead() and createTFoot() methods to create or return the thead or tfoot respectively, as well as the deleteTHead() and deleteTFoot() methods to delete them. There are also the createCaption() and deleteCaption() methods to create/return and delete the caption. These methods do not work correctly in Internet Explorer on Mac. Adding tbodies can only be done with basic DOM methods - there is no dedicated method.
Adding a row to a table
- 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'));
Rows have the rowIndex property, giving their index within their table, and the sectionRowIndex property, giving their index within their thead/tfoot/tbody.
The thead, tfoot and tbodies each have the dedicated method insertRow which can be used to add rows in all current browsers. This accepts a parameter; the 0-based index of where to add the new row (or -1 to append it at the end). They have the deleteRow method to delete them, which accepts the same index parameter, with -1 deleting the last row. These methods do not work correctly in Internet Explorer on Mac.
Adding one cell to every row in a table
- 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.
- Older Konqueror, Safari and OmiWeb get the cellIndex property wrong.
This example adds a new cell on the end of every row in a table. It assumes that table has both a thead, and a tfoot. Again, there are dedicated methods for this, but these fail in Internet Explorer on Mac, so they are not used here:
var theTable = document.getElementById('tableId');
for( var x = 0; x < theTable.tHead.rows.length; x++ ) {
var y = document.createElement('td');
y.appendChild(document.createTextNode('Thead cell text'));
theTable.tHead.rows[x].appendChild(y);
}
for( var z = 0; z < theTable.tBodies.length; z++ ) {
for( var x = 0; x < theTable.tBodies[z].rows.length; x++ ) {
var y = document.createElement('td');
y.appendChild(document.createTextNode('Tbody cell text'));
theTable.tBodies[z].rows[x].appendChild(y);
}
}
for( var x = 0; x < theTable.tFoot.rows.length; x++ ) {
var y = document.createElement('td');
y.appendChild(document.createTextNode('Tfoot cell text'));
theTable.tFoot.rows[x].appendChild(y);
}
Cells have the cellIndex property (except in early Konqueror versions), giving their index within their row.
Rows have the dedicated method insertCell which can be used to add cells in all current browsers. This accepts a parameter; the 0-based index of where to add the new row (or -1 to append it at the end). The added cell will always be a td, not a th. Rows have the deleteCell method to delete them, which accepts the same index parameter, with -1 deleting the last row. These methods do not work correctly in Internet Explorer on Mac.
DOM CSS
Individual element styles
- 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 style attribute, so styles can only be read if they were set using that attribute. Styles are almost all read/write.
The syntax is simple.
Simply write element.style.styleName.
If the style name consists of two or more words separated by hyphens '-', remove the hyphen and capitalise the first letter
of the word following it. For example, background-color becomes backgroundColor. When setting a value, it should be set as a string. Other data types will be converted to strings before setting the value to that string. Although most browsers will allow you to delete a style by setting it to null, a browser could legitimately set it to the string value 'null'
, and that would have an unwanted effect. If you want to delete a style, set it to an empty string, and never set it to null.
The only odd style is float, which is a reserved word in many languages, including JavaScript. As a result, the float style must be set using cssFloat in standards compliant browsers, and styleFloat in Internet Explorer 8- and 9+ in quirks mode (some standards compliant browsers also provide this as well). Simply set both of them. It will not cause any problems, and will work in all possible browsers.
The W3C DOM does provide an alternative way to change the styles on individual elements. The class attribute of the element can be removed using this:
element.className = '';
And it can be changed using:
element.className = 'newClassName';
Reading applied element styles
- 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 the window.getComputedStyle method. This is supported by Opera 7+, Mozilla, iCab 3+, Konqueror 3.5+, and Chrome/Safari 3+, as well as Internet Explorer 9+ in standards mode. Internet Explorer also offers an alternative that also works in quirks mode; the currentStyle property of an element. This is supported by Internet Explorer on Windows and Mac, Opera 9+ and iCab 3.
getComputedStyle requires two parameters. The first is a reference to the element. The second is either the name of a pseudo element (':before', ':after', ':first-line'), or null just for the element itself. Since currentStyle only allows you to obtain the style for the element itself, that is all I will show here. In most cases, the values returned by these are the same, but there are a few cases where they may be different. For example, a table may have a width set by the CSS, but may be stretched wider to fit its contents. In this case currentStyle will still return the width specified by the CSS, but getComputedStyle will return the new stretched width.
var oElement = document.getElementById('mydiv'), oColor;
if( window.getComputedStyle ) {
oColor = window.getComputedStyle(oElement,null).color;
} else if( oElement.currentStyle ) {
oColor = oElement.currentStyle.color;
}
As well as being an actual usable value, the returned value for several styles may be their default values, such as 'auto', 'normal', or 'inherit'. The browser may also choose to return values in any available unit. For example, even if the CSS specifies a length in 'em' units, the browser may still return the equivalent value in 'px' units.
In cases where currentStyle gives a different result to getComputedStyle, Internet Explorer offers an alternative; runtimeStyle. This is an object almost exactly like currentStyle, but it gives a computed value instead of a cascaded value. However, it does not always give a usable value, and often gives a different kind of result to what would be expected from getComputedStyle, if it gives any value at all. It often computes a blank string for unspecified values, instead of their default values. Use this with care and only if you are sure it gives the result you need.
It is also possible to obtain some details of an element using its offset, client, and scroll values. These are all available as properties of the element, and give its current dimensions in pixels, not the styles that apply to it. This means that they also give real dimensions, even if the CSS value is 'auto'. These are not reliable for inline elements, and should only be used for elements that have block or equivalent display. Note that these properties were not standardised for a long time (they will be part of future DOM standards), but they work fairly reliably cross browser.
- offsetWidth and offsetHeight
- The dimensions of the element taken outside its border. (The width inside the padding in IE's quirks mode.)
- clientWidth and clientHeight
- The dimensions of the element taken inside its border, and minus any scrollbar size.
- scrollWidth and scrollHeight
- The dimensions the element would be using if it did not have a scrollbar and was not constrained in size (in other words, the dimensions of its contents, plus its padding). Only usable and reliable if the element actually has a scrollbar.
- scrollLeft and scrollTop
- The distance the element has been scrolled.
Rewriting stylesheets
The DOM allows the stylesheets themselves to be changed. Some browsers allow stylesheets to be modified, some also allow rules to be created or removed. This will be covered in the next chapter of this tutorial.
Changing the href of a linked stylesheet
The href of the stylesheet can be read in some browsers using the href property. Opera 7+, Mozilla, Konqueror, Safari/Chrome and Internet Explorer 5+ can set the href of the link element using setAttribute on the link tag, and Internet Explorer 4+ can also set the href of the styleSheets[index].
Switching stylesheets
- 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/Chrome/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/Chrome do not allow the user to choose stylesheets in any menus, so you will have to use the DOM to change them manually. Note that when using the view menu or the DOM technique, stylesheets that share the same title will be treated as if they were the same stylesheet and will be switched together.
Using the view menu only allows you to select stylesheets whose titles are identical. If you want to enable combinations, you would either have to produce combined stylesheets or include the same sheet multiple times with different titles to allow all combinations. This is also a problem with most basic stylesheet switching scripts. The script I will show you allows you to choose a combination of stylesheet titles, making it superior to most other scripts.
Referencing the stylesheet's disabled property and title property
document.stylesheets
Microsoft first came up with the document.styleSheets collection. The W3C decided this was a good idea and introduced it in DOM Level 2 CSS. All stylesheets are available in this collection, even if the rel attribute is set to 'alternate stylesheet'. Setting the disabled property of the collection entry disables and enables the stylesheet, and the title attribute is available here as a JavaScript property.
- 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/Chrome [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/Chrome [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:
- Use the link and style tag technique in Opera 9+.
- 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:
- Use the link and style tag technique in Gecko.
- Use the document.stylesheets collection in Gecko.
- Either of:
- 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.
- 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+, Mozilla/Firefox and Internet Explorer 9+ (in standards mode). It is partially supported by Safari, Chrome, Konqueror and ICEbrowser, but their support is so bad it is largely unusable. I will give details of what fails in these browsers for each operation. Supposedly you can use the hasFeature method to check if a browser supports DOM 2 Style Sheets well enough, but unfortunately Safari/Chrome and ICEbrowser return true for the StyleSheets and CSS features. Their support is so poor that this is an insult to the spec. I suggest you ignore Safari/Chrome, Konqueror and ICEbrowser for now, and hope that their support improves in the future.
If you need compatibility with Safari/Chrome, Konqueror, ICEbrowser, or any browser that does not support DOM Style Sheets (like iCab or Opera 8 or below), then you should ensure that your script does not requre document.styleSheets to be supported, and that if it uses it, it does not produce errors when something fails in one of these browsers.
Internet Explorer 8- does not comply with the standard here, so if you are testing, use a browser that supports the standard correctly, such as Opera 9+ or Mozilla. I will give details later on how to get support for Internet Explorer 8- as well. Internet Explorer 9+ only adds support for DOM Style Sheets in standards mode. In quirks mode or IE 7 compatibility mode, only the Internet Explorer model is provided, along with its limitations. Internet Explorer on Mac supports large parts of the Internet Explorer model, but it also provides the cssRules collection, even though it does not use it according to the spec. I recommend you ignore IE Mac, but I will include it in the Internet Explorer section later on in this chapter.
DOM stylesheets does not provide an exact copy of what you put in your stylesheet. It produces what the browser sees, and what it interprets. Rules that it does not understand are not included, whitespace may be added or removed, combined styles may be split into their components, and split styles may be combined. Styles that are not understood will be ignored. Comments will not be included. Do not expect to be able to recognise your own stylesheet.
Note that you should not try using DOM 2 Style Sheets until you are certain that the stylesheet has completed loading (typically this means waiting for the document's onload event to fire). If you attempt to access it before then, the cssRules collection may be too short or may not exist.
The styleSheets list
- Mozilla gives STYLE element stylesheets the address of the current page as their href property, instead of null.
- Internet Explorer 9+ incorrectly claims that stylesheets are not disabled, even when their media attribute causes them to be disabled.
- 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
- Internet Explorer 9+ sometimes fails to redraw the page when media types are changed on linked stylesheets, but will update each part of the page as it reflows.
- Internet Explorer 9+ fails to recognise when mediaText is changed on stylesheets added with style tags, unless the tag originally had a media attribute.
- Internet Explorer 9+ incorrectly allows scripts to set invalid media types.
- Internet Explorer 9+ incorrectly returns an empty string when reading media.item(index) for out-of-bounds indexes.
- Opera 11+ fails to recognise when a change in mediaText causes the stylesheet not to apply any more.
- 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 media attribute. Note that browsers are free to add or remove spaces, as they decide is appropriate. They may also remove any media types they do not understand, or may rename them (typically to 'unknown'). If the text contains the same media type more than once, the browser may remove the extra copies. It is possible to set this text to a new string if desired:
document.styleSheets[0].media.mediaText = 'print,handheld';
Modifying the media types that the stylesheet applies to is mostly useful if the browser supports multiple media types. Of the browsers that support DOM stylesheets, only Opera supports more than just screen and print (Opera also supports projection, handheld, speech, and on some devices, tv). This means that currently, this feature is most useful in Opera, but can also be useful in other browsers if you wish to change between 'all', 'screen', and 'print'. Note that the browser may support media queries, and the specification never took these into account. Currently this affects Opera. Opera normalizes media queries (removing spaces and reformatting), to make sure that only one copy of each equivalent query (with parameters in the same order) exists.
If you set the mediaText to a value containing media types the browser does not recognise, it will allow that, and simply ignore unknown media types. Setting the media text to a value that the browser does not recognise as valid syntax will throw an error, so browsers that do not understand media queries will not allow you to set mediaText containing a media query. You will need to use a try...catch
statement around the assignment.
The media object also provides the appendMedium method to allow you to append one media type at a time, and the deleteMedium method to delete one at a time. Deleting a medium that is not in the list, or that the browser does not understand, will cause it to throw an error. Note that a stylesheet with no media is applied to all media types. Adding a medium to a stylesheet that has no specified media will cause it to be applied only to that media type. Deleting all media types from the list will cause the media text to be empty, so it will be applied to all media types.
document.styleSheets[0].media.appendMedium('handheld');
document.styleSheets[0].media.deleteMedium('print');
You can also step through all the listed media types using the item method, and check how many are in the list by checking the length parameter.
for( var i = 0; i < document.styleSheets[0].media.length; i++ ) {
alert(document.styleSheets[0].media.item[i]);
}
Test it here:
- 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/Chrome, Konqueror, Internet Explorer 9+ 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/Chrome, Konqueror, Internet Explorer 9+ and ICEbrowser cannot rewrite rules using cssText.
- Internet Explorer 9+ retains unrecognised styles in the cssText, instead of removing them.
- 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 the parentStyleSheet property, which is a reference to the stylesheet they are inside. Typically, you would need to use the type property to work out what other properties you will be able to access.
The rule also has a cssText property that gives a text representation of the rule. This may have whitespace added or removed, and styles may be split or combined into component styles. Multiple copies of the same style may be replaced with a single copy of the one that is actually used. In most cases, it no longer looks like the rule that was in the original stylesheet, but it should have the same effect in that browser (though not necessarily in other browsers) as the original rule.
var theRule = document.styleSheets[0].cssRules[0];
alert('Type: '+theRule.type+'\nRule: '+theRule.cssText);
Test it here: alert the first rule in the first stylesheet of this page.
In theory it is possible to set the cssText to a new value, as long as the new value can be interpreted as the same type of rule as the original rule (so it is not possible to convert a @import
rule into a @media
rule, for example). Unfortunately, browser support is not good enough to use this at the moment.
document.styleSheets[0].cssRules[0].cssText = 'body { background: #779; }';
If the rule is a @charset
rule, it will have an encoding property, giving the encoding string, which can be rewritten if desired. Unknown @ rules will have no other properties. All other rule types will be covered below:
Normal style, @page, and @font-face rules
- 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). - Opera 10+ does not like having existing style values set to an empty string, and may not update the rendering - set them to an actual value instead.
- 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
- Internet Explorer 9+ throws an error if you attempt to access the media attribute of an import rule.
- Safari/Chrome, 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/Chrome 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 media property, which also functions like that of the StyleSheet object.
All rules inside the @media
block also have the parentRule property, which references the @media
rule that they are inside. @media
blocks may contain other @media
blocks, and these can be nested as much as needed. If they are nested, their rules must be accessed by going through the chain of cssRules collections.
alert(document.styleSheets[0].cssRules[0].cssRules[0].cssText);
Test it here: show the contents of the last recognised media block in the first stylesheet.
Getting support for legacy Internet Explorer
Internet Explorer 8- on Windows and Mac does not provide many of the DOM 2 Style Sheets methods or properties, but it has an alternative that is not as complete as the standard, but can handle basic stylesheet manipulation. Internet Explorer 9+ follows the DOM standard only in standards rendering mode. Pages rendered in quirks mode will need to use the Internet Explorer model instead. Pages rendered in IE9+'s IE 7 compatibility mode (enabled automatically or by the user on some pages, or by default for intranet sites) will also not be able to use the standard model. If you need Internet Explorer 8- (or 9+ quirks mode) support, you will have to accept the differences and limitations, make sure your code does not rely on the parts that Internet Explorer is missing, and implement the branching code I will show you here.
In standards rendering mode, Internet Explorer 9+ maintains both the DOM and IE models, which are completely separate. When referencing the same rule using both collections, the returned objects are not the same as each other. The common properties in both models (like selectorText) can hold completely different values, depending on the bugs present in the implementation of that model. This can catch scripts out very easily, so it's best to use the least buggy model - the DOM model.
The styleSheets list
- Internet Explorer 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 8- 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. (Note that in IE 9+ standards mode, IE provides both models - the media attribute follows the standards model.)
var oSheet = document.styleSheets[0];
if( typeof(oSheet.media) == 'string' ) {
try { oSheet.media = 'screen'; } catch(e) {}
} else {
oSheet.media.mediaText = 'screen';
}
Test it here: set the media type on all stylesheets on this page to 'print'
(the page should appear unstyled when viewed normally), use print preview to check if it worked, then return it to normal.
Each stylesheet can reference the element that creates it using owningElement, in the same way as standards compliant browsers use ownerNode.
var oSheet = document.styleSheets[0];
var oElement = oSheet.ownerNode ? oSheet.ownerNode : oSheet.owningElement;
Although IE on Windows provides the styleSheet property of the LINK or STYLE element, in the same way as standards compliant browsers use sheet, this property is not available in IE on Mac.
var oElement = document.getElementsByTagName('style')[0];
var oSheet = oElement.sheet ? oElement.sheet : oElement.styleSheet;
Test it here: check for these properties.
Stylesheet cssRules collection
- Internet Explorer on Mac cannot add or remove rules.
Internet Explorer provides the rules collection, in the same way as standards compliant browsers use cssRules. However, its behaviour is sufficiently different and incompatible, so it is not possible to simply index the same rule in each collection. @page
, @media
, @import
, @charset
, and @font-face
rules are not included in the collection. In IE on Windows, all rules inside a @media
block are included in the rules collection of the stylesheet. In IE Mac, they are ignored completely, and are not available to the DOM. In both browsers, it is not possible to modify the @media
rule itself. Adding new rules into a @media
block is not possible in IE on Windows.
Note that the stylesheet itself has a cssText property in IE, which gives all the CSS in the stylesheet. On Windows, this includes any @media
blocks. However, it is not much fun to edit the stylesheet using pattern matching, which is all that this can provide.
IE on Mac provides both the rules and cssRules collections, but treats them both the IE way (so the cssRules collection is too short). Internet Explorer 9+, also provides both collections in standards rendering mode, but this time, they are implemented completely separately - using the IE and DOM models. In IE 9+, you would almost certainly want to use the standard one where it is available, while IE on Mac would want to use the IE model. If you don't care about IE on Mac (it's been discontinued, so you probably don't care about it), then simply check for the DOM collection and use it where available, falling back to the IE model otherwise:
var oSheet = document.styleSheets[0];
var oRule = oSheet.cssRules ? oSheet.cssRules[7] : oSheet.rules[3];
However, it is possible to get support for IE on Mac. Check if the stadard DOM collection is available, and make sure that it is not the same as the IE collection (that check will also work in other browsers that do not provide the DOM collection, since the IE collection will be undefined):
var oSheet = document.styleSheets[0];
var oRule = ( oSheet.cssRules && oSheet.cssRules != oSheet.rules ) ? oSheet.cssRules[7] : oSheet.rules[3];
Adding and removing rules in Internet Explorer (on Windows) is done using the addRule and removeRule methods. The removeRule method is exactly the same as the standard deleteRule method (except of course that the index will be different). The addRule method, however, is very different to the standard insertRule method. Firstly, it requires two different parameters; the selector string, and the rule string. Secondly, it can only add a rule at the end of the stylesheet.
var oSheet = document.styleSheets[0];
if( oSheet.deleteRule ) {
oSheet.deleteRule(7);
} else if( oSheet.removeRule ) {
oSheet.removeRule(3);
}
if( oSheet.insertRule ) {
oSheet.insertRule('body { background: #779; }',oSheet.cssRules.length);
} else if( oSheet.addRule ) {
oSheet.addRule('body','background: #779;');
}
Test it here: add the new rule 'p { color: lime; }'
, and then delete it again.
The CSS rule
- selectorText only includes everything upto the first comma in IE 8-, and after the last comma in IE 9+.
- 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 the parentStyleSheet property.
They also have the all important style property. However, here there are some significant differences. The item method does not exist in IE on Windows. You can try using for(i in styleobject)
but that does not return what you specify in the stylesheet. Instead, it steps through all possible styles that IE recognises, whether you actually used them or not. It is actually a fairly short list (since IE does not actually support very many styles), but it makes recognising your own styles very hard.
More importantly, however, IE does not provide most of the other methods of the style object. To read or change styles, you will have to stick to the .style.color
syntax. Since this works in all browsers, that is not too difficult to work with. Note that IE will throw errors if you set styles to values that it does not support (such as setting display to 'table'). Note that the .style.cssText property is available, but IE Mac makes a bit of a mess of it.
var oSheet = document.styleSheets[0];
var oRule = oSheet.rules ? oSheet.rules[3] : oSheet.cssRules[7];
oRule.style.color = 'lime';
Test it here: the last rule in the demo stylesheet is currently 'p { }'
. Set its color property to 'lime'
, then set it back to ''
.
There is also a StyleSheet.pages collection that gives all @page rules, but it does not give any useful information about the styles in that rule.
@import rules and imported stylesheets
@import
rules are listed in their own StyleSheet.imports collection in IE. Each entry in this collection is a StyleSheet object, that is the same as the styleSheet property of the @import
rule in standards compliant browsers. It has the same properties as a normal stylesheet.
var oSheet = document.styleSheets[0], oImportedSheet;
if( oSheet.imports ) {
oImportedSheet = oSheet.imports[0];
} else {
oImportedSheet = oSheet.cssRules[0].styleSheet;
}
DOM events
This is currently supported by Opera 7+, Mozilla/Firefox, Konqueror 3.0+, Safari/Chrome, Internet Explorer 9+ in standards mode and NetFront 3.3+. It is also partially supported by iCab 3, ICEBrowser 5+, and Tkhtml Hv3. Espial claim that Escape/Evo 5 supports this, but it does not support even the basics.
Note: Internet Explorer 8- on Windows does not comply with the standard here, so if you are testing, use a browser that supports the standard correctly, such as Opera. Internet Explorer 9+ drops support for the standard model in quirks mode and IE 7 compatibility mode, and will continue to need the Internet Explorer 8- workarounds. I will give details later on how to get support for Internet Explorer 8- on Windows as well. Internet Explorer on Mac does not support either the DOM or IE events models.
Problems with the traditional event models
Traditionally, browsers had a simple way to listen for events, and run scripts when that happened. The script could be held inside an appropriate event handler attribute, such as onclick="alert('here');"
. Internally, the browser would register this as an anonymous function, that a script could override:
referenceToElement.onclick = function () { alert('here'); };
Returning false from any event handler function would prevent the normal action from happening, such as preventing the browser following a link when it detected the click event.
In many basic applications, this was adequate. However, it has limits. If an event handler already exists, and you want to add functionality to it part way through your script, you would have to rewrite the function with both the new and old functionality. Then if you wanted to remove some functionality, you would have to rewrite it again. You might even have to write your own handler function management system, with one event handler that runs the appropriate functions in turn.
Another problem with the traditional model was that it never coped well with the possibility that two elements may detect the same event. Imagine that a link is inside a div, and both the link and the div are set up to detect the click event. When you click the link, should the link see the click? Or should the div see it? Or should both? If both should detect it, in what order should they see it; div first, or link first?
Initially, when Netscape introduced events, their answer was simple; the link should see it. Nothing more. Later, they allowed the div to see it as well, but only if you told it to capture the event. The div would then see the click, and after that, the link would see it (in Netscape's original implementation, the event would only be seen by the link if you explicitly told it to continue after the initial capture - this is not necessary in DOM 2 events). At the same time, Internet Explorer introduced an alternative version. By default, both the link and the div would see the click, and unlike in Netscape, the link would see it first. This was called bubbling.
Adding DOM event listeners
- 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 for a single event, but you could also say in what order elements should see the event. The DOM refers to these event handler functions as event listeners. The way to add an event listener is like this:
referenceToElement.addEventListener('nameOfEvent',referenceToFunction,phase)
You can add as many listeners for the same event on the same element as you want, but note that there is no way to guarantee in what order they will be run. (Trying to add the same event, function, and phase to the same element multiple times will result in it being added just once.) Note that, unlike with the traditional events models, it is not possible to obtain a reference to the event handler functions (this could previously done with referenceToElement.oneventname, but DOM 2 events does not provide any equivalent to list all the event handlers).
The name of the event is written like this: 'click', 'mouseover', or 'keyup'. The referenced function will be run in much the same way as with the traditional model. According to the spec, the 'referenceToFunction' parameter should actually be a reference to an object that has a method called 'handleEvent', and that method will be called instead. However, in practice, Opera 8+, Mozilla/Firefox, Konqueror 3.2+, Safari/Chrome, Internet Explorer 9+ and iCab implement this correctly, but the others do not. Aditionally, most authors are not even aware that this functionality exists. In most cases, it is easiest to just use a direct function reference.
The phase says in what order the event should be detected. False means the bubbling phase, and is usually the one you want to use. True means the capturing phase. Any event handlers added using the traditional techniques are treated as a bubbling event listener. The phase is actually very important to get right, since its behaviour is not straightforward. Capturing is special. According to the specification, if an element is set up to capture a click event (for example), and you click it, the event handler should not be run. It should only be run if you click on a child element inside the element. In other words, the capture phase is only used on ancestors of the target element.
In our earlier link and div example, assume that both elements are set up to detect the click event using both the capturing and bubbling phase. If you click the link, the specification says that the event listeners should be run in this order:
- Capture listeners for the div
- Bubbling listeners for the link
- Bubbling listeners for the div
This progression from listener to listener is known as propagation. You can check what stage the propagation is at by checking the eventObject.eventPhase property. It should be 1 during the capture phase, 2 during the bubble phase on the target element itself (in this case the link), and 3 during the bubbling phase on the target's ancestors. It can also be 0 if it is a manually created event object that has not yet been fired. See below for further details.
Note that some events (such as the load event) do not bubble up to ancestors, but they will still capture, so capture phase listeners will always see the events of their children (with load events detected in the capture phase on the document, this means they should see an event every time any image, plugin, or many other embedded file types load).
Also note that in the above example, only Opera 9.2-, Safari 1.1-, NetFront 3 and ICEBrowser follow the specification. Mozilla/Firefox, Opera 9.5+, Konqueror, Chrome/Safari 1.2+, Internet Explorer 9+, Netfront 4+ and iCab will fire the capture listeners on the link. Mozilla 1.7-/Firefox 1.0 will fire it before point 3. Opera 9.5+, Konqueror, Safari, iCab and Mozilla 1.8+/Firefox 1.5+ before point 2 giving this (commonly supported) event propagation:
- Capture listeners for the div
- Capture listeners for the link
- Bubbling listeners for the link
- Bubbling listeners for the div
That behaviour is wrong according to DOM 2 Events, but it is relied on by many Web pages, so it it unlikely that the browsers will follow the DOM 2 Events specification. It may be changed to be correct in DOM 3 (though it currently is not), in which case the other browsers can be expected to implement it that way as well. However, you should be aware that browsers may behave either way. Do not rely on the capturing phase firing on the target, since it is currently incompatible, and may well be changed at some point.
So now, to put this example into practice:
<div id="thediv"><a href="/" id="thelink">test</a></div>
...
var oDiv = document.getElementById('thediv');
var oLink = document.getElementById('thelink');
oDiv.addEventListener('click',function (e) {
alert('1. Div capture ran');
},true);
oDiv.addEventListener('click',function (e) {
alert('3. Div bubble ran');
},false);
oLink.addEventListener('click',function (e) {
alert('Link capture ran - browser does not follow the specification');
},true);
oLink.addEventListener('click',function (e) {
alert('2. Link bubble ran (first listener)');
},false);
oLink.addEventListener('click',function (e) {
alert('2. Link bubble ran (second listener)');
},false);
Test it here: click this link to activate the event listeners.
Note that some browsers will run the first link bubble listener before the second, and some will run the second before the first - the standard does not say in which order they should be run, and it even allows them to be run at the same time - you should not rely on any specific behaviour here).
Removing event listeners
Removing event listeners is as easy as adding them:
referenceToElement.removeEventListener('nameOfEvent',referenceToFunction,phase)
Note that you can remove event listeners at any time, and you must remove them using the exact same parameters as you added them with. This can be difficult if you used anonymous functions as I did in the example above. If you use anonymous functions, you can use the arguments.callee property provided by JavaScript to reference the function and remove the listener. This reference can only be done from inside the listener itself. So for example, to add an event listener that removes itself after it has fired once, you can use:
oLink.addEventListener('click',function (e) {
alert('Link bubble fired');
this.removeEventListener('click',arguments.callee,false);
},false);
Test it here: fire the event listener and see if it can remove itself (you should get an alert the first time you click the link).
Referencing the elements that detect the event
You may notice that I use the 'this' keyword in the last example. Inside an event listener function, the 'this' keyword is a reference to the element that is currently detecting the event. In the first example, that could be the div, or the link, depending on which one the event listener is attached to. You may also notice that the functions accept a parameter (I called it 'e', but you could call it whatever you want). That parameter, as with the original Netscape event model, is the event object, and contains a lot of information about the event. For now, I will stick with the basics.
eventObject.currentTarget is the same as 'this' - it refers to the element that the listener function is attached to (if the same function is attached as a listener to several elements, it will be a reference to the listener that is currently being run).
eventObject.target is the element that the event actually happened to. In the case of the earlier example, this would be the link. If there was a span inside the link, and you clicked that, then eventObject.target would be a reference to the span, even if the span itself did not have any listeners attached directly to it. Note that older Safari and Konqueror sometimes make the target point to the textNode inside the element. That is incorrect behaviour, and is fixed in current versions. If this is likely to cause problems, check the nodeType of the target. If it is a text or CDATA node, then you will need to use its parent node instead.
var theTarget = e.target;
if( theTarget && ( e.target.nodeType == 3 || e.target.nodeType == 4 ) ) {
theTarget = theTarget.parentNode;
}
Test it here: click anywhere within this sentence to see what element you clicked.
Some events (such as mouseover) will also provide eventObject.relatedTarget. This will be a reference to the element the mouse was over before it was moved over the current element. With mutation events, it is typically (but not always) the parent node of the node being manipulated.
Obtaining other information from the event
Information such as mouse button and position, keyCode, ctrlKey, altKey, shiftKey, and detail (such as click count) are available in the same way as with the older model (see the HTML events section of this tutorial for more details). They are available as properties of the event object. Mutation events can also provide prevValue, newValue, attrName and attrChange values (see the DOM 2 events specification for more details).
Preventing the default action
In the example above, clicking the links will activate the listeners, but the link will be followed anyway. Returning false from a listener function does not work like it used to in the traditional models. Instead, you must use eventObject.preventDefault()
- only one event listener needs to run this method for the default action to be prevented.
Not all event types can be cancelled (the load event, for example, has no default action, and cannot be cancelled). If an event is of a type that can be cancelled, the eventObject.cancelable property will be true.
oLink.addEventListener('click',function (e) {
alert('Link detected a click. Cancellable: '+e.cancellable);
e.preventDefault();
},false);
Test it here: if you click this link, you should remain on this page.
Preventing other listeners from seeing the event
- 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 the stopPropagation method, the other one will always run, no matter in what order the browser ran them. The method would only stop the event listener that was added to the div in the bubble phase.
<div id="thediv"><a href="/" id="thelink">test</a></div>
...
var oDiv = document.getElementById('thediv');
var oLink = document.getElementById('thelink');
oDiv.addEventListener('click',function (e) {
alert('Div capture ran. Now stopping propagation.');
e.stopPropagation();
},true);
oLink.addEventListener('click',function (e) {
alert('This alert should never appear');
},false);
Test it here: click this link to activate the event listeners.
More than basic events
As well as the basic events defined in HTML, DOM 2 provides aditional event types that can be detected. A useful example of these are the mutation events. These fire when elements or descendant nodes are added, modified, or removed by a script (I will not go into more detail here, see the DOM 2 events specification for more details). They are partially supported by Opera, Mozilla/Firefox, Konqueror, Safari/Chrome and Internet Explorer 9+. Note that browser support for 'DOMNodeInsertedIntoDocument', 'DOMNodeRemovedFromDocument', and 'DOMSubtreeModified' events is currently very poor, so these events should not be used. Safari 4- also does not work with the 'DOMAttrModified' event. Note that Opera 7.1, Safari 1.2, and Konqueror 3.2-3.3 will crash if you attempt to use any mutation events. Opera 7.2+, Safari 1.3+, and Konqueror 3.4+ will work correctly. Konqueror 4.2.2 seems to have lost all support for all mutation events.
The DOM does not provide any way to work out if the event will fire or not. If a browser does not support an event, it will still allow you to add the event listener, but it will not fire the event. It is possible to check if a browser supports the required events model using hasFeature, but this is not a perfect indication, since a browser will only claim to support the feature if its support is relatively complete. Browsers may support a large amount of the feature (enough for what you need) but still not claim to support it. Assuming you want a relatively complete implementation, instead of support for just one or two event types, checking for mutation events is as simple as this:
if( document.implementation.hasFeature('MutationEvents','2.0') )
Note that this will exclude Mozilla/Firefox, even though it does support many of the common mutation events. You could alternatively check for the interface as well, since Mozilla/Firefox (Opera, newer Safari/Chrome and Internet Explorer 9+ provide it too, but not Konqueror or older Safari) should provide this:
if( document.implementation.hasFeature('MutationEvents','2.0') || window.MutationEvent )
Some other types of event may also cause problems, not because the browsers do not support them, but because the browsers support them only on the wrong object. The load event is the worst behaved in this respect. According to the original DOM 2 events specification, the load event should be detected on the document object. Unfortunately, the traditional models used the body tag, and mapped it to the window object. As a result, when browsers added support for it via DOM, most of them initially chose to detect it only on the window object. Future specifications will make it more clear that it should only work with the window object.
Opera 10.1-, and similarly aged versions of Konqueror, Safari/Chrome and NetFront correctly detected it on the document object (as well as on the window object, for compatibility reasons). This was changed in Opera 10.5+, and similarly aged versions of the other browsers, to follow the Mozilla behaviour. Internet Explorer 9+ also follows the Mozilla behaviour. New implementations may theoretically follow the DOM specification, and only detect it on the document object. For this reason, you should check if the window and document objects can detect events before adding listeners to them. If the browser allows you to attach event listeners to the window, you can assume that it can detect the load event there. This is true for all current implementations.
Note also that as described above, you should always use the bubble phase when detecting load events, or they will repeatedly fire when other embedded content loads. However, note that Mozilla/Firefox and iCab 3 cannot capture load events, Opera 9.5+, Safari/Chrome, Internet Explorer 9+ and Konqueror only capture load events when the listener is attached to the document (not the window), and only Opera 9.2- and ICEBrowser correctly always capture load events. Unless you really know what you are doing, and you have worked around all the incompatibilities, you should only ever detect load events in the bubbling phase. Most importantly, do not rely on the implementations of browsers that do not capture load events; they are wrong, and the same code will not work the same way in other browsers.
if( window.addEventListener ) {
window.addEventListener('load',handlerFunction,false);
} else if( document.addEventListener ) {
document.addEventListener('load',handlerFunction,false);
}
Bad news; your browser did not detect the load event using this script.
The situation is similar for several other events, such as scroll and resize. They will also require you to use the window and document branches.
Manually firing events
- Safari/Chrome 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, using referenceToElement.oneventname()
- since there was only one possible handler, and it was treated as a method, it could be run as easily as any other method. With the DOM 2 events model, this is not possible, since there may be multiple handlers for the same event. The DOM provides a few methods that can be used to create and prepare a fake event object, then use it to fire the event on any target.
The element you fire an event on does not have to be listening for that event, since potentially, the parent element may also be listening for that event.
Note that manually firing an event does not generate the default action associated with that event. For example, manually firing a focus event does not cause the element to receive focus (you must use its focus method for that), manually firing a submit event does not submit a form (use the submit method), manually firing a key event does not cause that letter to appear in a focused text input, and manually firing a click event on a link does not cause the link to be activated, etc. In the case of UI events, this is important for security reasons, as it prevents scripts from simulating user actions that interact with the browser itself.
There are five DOM 2 event modules, and they map directly to the names of the DOM features;
- Events
- Covers all event types.
- HTMLEvents
- Covers 'abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', and 'unload'.
- UIEvents
- Covers 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', and (since they do not have their own key events module in DOM 2) it also covers 'keydown', 'keypress', and 'keyup'. Also indirectly covers MouseEvents.
- MouseEvents
- Covers 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', and 'mouseup'.
- MutationEvents
- Covers 'DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMCharacterDataModified', 'DOMNodeInsertedIntoDocument', 'DOMNodeRemovedFromDocument', and 'DOMSubtreeModified'.
Other events (such as 'dblclick') are not part of the specification, and browsers are not required to support them. Browsers that do support them will either choose an appropriate event module to class them as - dblclick would fit very well into MouseEvents - or they will create a new module. You will need to know what module the browser has put the events into when you create these events.
Fake event objects are created using the document.createEvent method, and the appropriate event module should be specified when creating it. They are then prepared using the appropriate method, and finally they are then fired on the desired target element using the element.dispatchEvent method. If you use the more specific event type instead of the generic event type, you can provide more information when preparing it. dispatchEvent will return false if the detault action was prevented.
The event object methods for preparing the event object properties for the various event modules are:
- HTMLEvents and generic Events
initEvent( 'type', bubbles, cancelable )
- UIEvents
initUIEvent( 'type', bubbles, cancelable, windowObject, detail )
- MouseEvents
initMouseEvent( 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget )
- MutationEvents
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)initMutationEvent( 'type', bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange )
MouseEvents are a subclass of UIEvents, and all event modules are a subclass of Events, so if you create a mouse event object, it will also inherit the initUIEvent and initEvent methods from the more generic UIEvents and Events interfaces. You can choose which init*Event method you want to use when preparing the event.
The following is an example of firing a click event:
var fireOnThis = document.getElementById('someID');
var evObj = document.createEvent('MouseEvents');
evObj.initMouseEvent( 'click', true, true, window, 1, 12, 345, 7, 220, false, false, true, false, 0, null );
fireOnThis.dispatchEvent(evObj);
Test it here: move your mouse over here and I will manually fire a mouseup event instead.
The following is another example of firing a click event, this time using the generic initEvent method, and leaving undefined properties at their default values (even when using the generic initEvent or initUIEvent, you should still use the exact module name, and not a generic module, or it will not work in Mozilla/Firefox, Konqueror, or Safari/Chrome):
var fireOnThis = document.getElementById('someID');
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( 'click', true, true );
fireOnThis.dispatchEvent(evObj);
Key events are a little harder, because Mozilla/Firefox does not implement the standard correctly, and does not allow you to create key events using generic UIEvents (it may just crash). Instead, it provides a proprietary module 'KeyEvents', and initialisation method 'initKeyEvent'. The proprietary initKeyEvent method expects the following parameters: 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode. You can use if(window.KeyEvent)
to detect support for the KeyEvents module. Browsers that expect you to use UIEvents do not have a specific initialisation function for key events, so some properties must be added manually:
var fireOnThis = document.getElementById('someID');
if( window.KeyEvent ) {
var evObj = document.createEvent('KeyEvents');
evObj.initKeyEvent( 'keyup', true, true, window, false, false, false, false, 13, 0 );
} else {
var evObj = document.createEvent('UIEvents');
evObj.initUIEvent( 'keyup', true, true, window, 1 );
evObj.keyCode = 13;
}
fireOnThis.dispatchEvent(evObj);
Getting support for legacy Internet Explorer on Windows
Internet Explorer 8- on Windows does not provide any of the DOM 2 Events methods, and provides very few of the properties of the event object. Internet Explorer 9+ also does not support the DOM 2 Events methods on pages that trigger quirks mode. Pages rendered in IE9+'s IE 7 compatibility mode (enabled automatically or by the user on some pages, or by default for intranet sites) will also not be able to use the standard model. Version 5.0+ (including IE9+ in quirks or standards mode) does provide a similar system that is much more limited in its capabilities, but if you need legacy Internet Explorer support, you will have to accept the differences and limitations, make sure your code does not rely on the parts that Internet Explorer is missing, and implement the branching code I will show you here.
Adding and removing listeners
Adding and removing event listeners is done using these methods:
referenceToElement.attachEvent('onNameOfEvent',referenceToFunction);
referenceToElement.detachEvent('onNameOfEvent',referenceToFunction);
These are almost synonymous with the addEventListener and removeEventListener methods, but they are unable to specify a phase. The Internet Explorer model only detects events in the bubbling phase, not capture. It also requires 'on' to be included at the start of the eventname, so for the click event, you should use 'onclick' in IE, and not 'click'. (Note also that IE 8- only supports the basic event types - it does not support any of the advanced types, such as mutation events.) If you need legacy Internet Explorer support as well, you should use this code:
function eventHandler(e) {
alert('Div bubble ran');
}
var oDiv = document.getElementById('thediv');
if( oDiv.addEventListener ) {
oDiv.addEventListener('click',eventHandler,false);
} else if( oDiv.attachEvent ) {
oDiv.attachEvent('onclick',eventHandler);
}
Test it here: this text has been set up to detect click events, and should alert a message when clicked.
Referencing the elements that detect the event
Internet Explorer's model is more annoying here. The 'this' keyword is a reference to the window object. There is no equivalent to the eventObject.currentTarget property. Internet Explorer's model makes it impossible to see what element is currently processing the event.
It is possible to reference the actual target of the event, using the eventObject.srcElement property, which is equivalent to the eventObject.target property provided by DOM 2 Events. Unlike the way it works with traditional event models, IE does correctly provide the event object as an argument to the handler function when you use attachEvent. There is no need to check if window.event is needed instead, because it is not needed. However, it is important to note that in IE, the event object only exists for as long as the initial event propagation thread. It cannot be stored and reference later, or used in timeout threads. To use srcElement, simply detect what you need (make sure the standards compliant check is made first):
var theTarget = e.target ? e.target : e.srcElement;
if( theTarget && ( theTarget.nodeType == 3 || theTarget.nodeType == 4 ) ) {
theTarget = theTarget.parentNode;
}
Test it here: click anywhere within this sentence to see what element you clicked.
You can also do the same to get the equivalent to eventObject.relatedTarget - which is known as eventObject.fromElement in IE. It is available for mouseover and mouseout events.
var relTarget = e.relatedTarget ? e.relatedTarget : e.fromElement;
Preventing the default action
The IE model does not provide eventObject.preventDefault() but provides an alternative. The event object has a property called 'returnValue' that will perform the equivalent functionality if it is set to false. Setting this value in other browsers will not cause any problems, so the easiest way to support IE as well is like this:
if( e.preventDefault ) { e.preventDefault(); }
e.returnValue = false;
Test it here: if you click this link, you should remain on this page.
It is not possible to check if the event is a type that can be cancelled in IE.
Preventing other listeners from seeing the event
The IE model does not provide the stopPropagation method (probably because it only supports bubbling). Instead, it uses a property called 'cancelBubble', which must be set to true to prevent propagation. It is not possible to check if the event is a type that can bubble in the IE model.
if( e.stopPropagation ) { e.stopPropagation(); }
e.cancelBubble = true;
Test it here: click this link to activate the event listeners.
Detecting the load event
Legacy Internet Explorer detects the load event on the window object, using the usual attachEvent method. This makes the load event listener (shown above) require one more branch:
if( window.addEventListener ) {
window.addEventListener('load',handlerFunction,false);
} else if( document.addEventListener ) {
document.addEventListener('load',handlerFunction,false);
} else if( window.attachEvent ) {
window.attachEvent('onload',handlerFunction);
}
Bad news; your browser did not detect the load event using this script.
Manually firing events
This is a little more messy than the other event handling, since the initialisation is considerably different. The Internet Explorer model has a method for manually firing events, called fireEvent, and it is available in IE 5.5+ (there is no equivalent in IE 5.0). In its simplest form, this is similar to the DOM version, but the bubbles and cancelable properties are not available. The fireEvent method expects to be passed either one or two parameters. The first should be the name of the event (for example; 'onchange'), and the optional second parameter should be an event object to be passed to the handler.
Note that even though the window object can detect several events in IE (such as load, unload, scroll, and resize - all of which should, according to the spec, be detected by the document object, not window), it is not possible to use fireEvent on the window object.
var fireOnThis = document.getElementById('someID');
if( document.createEvent ) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( 'mousemove', true, false );
fireOnThis.dispatchEvent(evObj);
} else if( document.createEventObject ) {
fireOnThis.fireEvent('onmousemove');
}
If you want to specify more details, you will need to use the createEventObject method in IE to replicate the createEvent and init*Event methods of the DOM. The createEventObject method normally returns a blank event object, and you will need to define parameters yourself. You can optionally pass an existing event object to createEventObject, and it will use that as a template when preparing the new event object. Usually, you will not need to pass it any arguments.
var fireOnThis = document.getElementById('someID');
if( document.createEvent ) {
var evObj = document.createEvent('MouseEvents');
evObj.initMouseEvent( 'mousemove', true, false, window, 0, 12, 345, 7, 220, false, false, true, false, 0, null );
fireOnThis.dispatchEvent(evObj);
} else if( document.createEventObject ) {
var evObj = document.createEventObject();
evObj.detail = 0;
evObj.screenX = 12;
evObj.screenY = 345;
evObj.clientX = 7;
evObj.clientY = 220;
evObj.ctrlKey = false;
evObj.altKey = false;
evObj.shiftKey = true;
evObj.metaKey = false;
evObj.button = 0;
evObj.relatedTarget = null;
fireOnThis.fireEvent('onmousemove',evObj);
}
Test it here: move your mouse over here and I will manually fire a mouseup event instead.
DOM objects and methods
This gives all properties, collections and methods of the W3C DOM that can be reliably used in all major DOM browsers, as well as document.styleSheets, which is much less reliable, but useful if it is available.
Contents
Key
- Parent object
- Child object
- Child property
- Child object being accessed through a collection[]
- Event
- 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 The document object for the current document - - read-only
- createDocumentFragment() Returns a document fragment that can be used to add multiple nodes to the document
- createElement('element_name') Returns a new element of type element_name
- createTextNode('text content') Returns a text node with a string value matching the text content
To walk the DOM tree
- document The document object for the current document - - read-only
- body The BODY element node - - read-only
- documentElement The HTML element node - - read-only
- getElementById('element id') Returns a reference to the element with the specified id
- getElementsByTagName('element_name') Returns a collection of all elements with the specified element name - NOTE: does not return a true collection; - returns an object with .0 ... .n and .length properties
DOM support
- document The document object for the current document - - read-only
- implementation An object giving information about the DOM implementation - (Also has other methods in good browsers [not covered here]) - - read-only
- hasFeature(string: feature,string: domVersion) Returns true if the browser supports that feature of the DOM specification - Do not rely on browsers to accurately represent their support for features
- implementation An object giving information about the DOM implementation - (Also has other methods in good browsers [not covered here]) - - read-only
All nodes
To walk the DOM tree
- IE 8- incorrectly provides element methods like getElementsByTagName on comment nodes.
- node An element, text node, document node, or any other node type - - read-only
- childNodes[] A collection of all text nodes and element nodes the node contains directly if node is an element node or document node - - read-only
- parentNode A reference to the containing node - - read-only
- firstChild The first entry in the childNodes collection if node is an element node or document node - - read-only
- getElementsByTagName('element_name') Returns a collection of all elements inside the current element - or document node, with the specified element name - NOTE: does not return a true collection; - returns an object with .0 ... .n and .length properties
- lastChild The last entry in the childNodes collection if node is an element node or document node - - read-only
- nextSibling The next entry in the childNodes collection of the parent node - - read-only
- offsetParent The node that the element's offset can be calculated from - - read-only
- previousSibling The previous entry in the childNodes collection of the parent node - - read-only
Attaching, copying or removing nodes
- node An element, text or attribute node - - read-only
- appendChild(nodeReference) Appends the referenced node to the end of the childNodes collection, and displays it on the page - If it is already attached to the document, it will be moved to this new location
- cloneNode(bool: copyChildrenToo) Returns a clone of a node or branch of the DOM tree, event handlers may or may not be cloned
- innerHTML The HTML contained by the element - Small amounts of HTML will be inserted into the DOM tree immediately - Large amounts of HTML may take as much as 1/2 second to be inserted into the DOM tree - Does not work with XHTML in some browsers - - read/write
- insertBefore(nodeReferenceX,nodeReferenceY) Inserts or moves node nodeReferenceX as a childNode of the element before nodeReferenceY
- removeChild(nodeReference) Removes the specified child from the childNodes collection (deletes it) - If childNodes.length becomes 0, the parentNode is also deleted
- replaceChild(nodeReferenceX,nodeReferenceY) Replaces child nodeReferenceY with node nodeReferenceX - Returns nodeReferenceY
- splitText(index) Splits a text node into two at the specified index creating a new entry in the childNodes collection
Node information
- IE 8- incorrectly provides element properties like tagName on comment nodes.
- node An element, text or attribute node - - read-only
- data Equivalent to nodeValue where node is a text node - - read/write
- hasChildNodes() Returns boolean if the node has child nodes
- id The id of the element - - read/write
- nodeName Name of the node (tag name, attribute name or '#text') - - read-only
- nodeType 1 for an element/tag, 2 or undefined for an attribute or 3 for a text node - - read-only
- nodeValue The string of a text node or attribute node - - read/write
- specified Boolean: says if the node has a value - - read-only
- tagName An uppercase representation of the tag name if node is an element node - - In theory, this should be lower case for XHTML (when served as XHTML, not HTML), but some browsers will still use upper case - - This property is incorrectly also provided on comment nodes in IE 8- - - read-only
- title The title attribute of the element - - read/write
Element node attributes
- node An element node - - read-only
- attributes[] A numerical collection of all attributes defined or that could be defined for the element - Difficult to use because of browser incompatibilities - - read/write
- getAttribute('attributeName') Returns the value of the specified attribute [not class, style or event handler]
- removeAttribute('attributeName') Removes the specified attribute [not align, class, style or event handler]
- setAttribute('attribute_name','attribute_value') Sets the name=value pair for the specified attribute of the element [not name, class, style or event handler] - Use along with style.textAlign to make align work in Opera 7.0-7.1
Element node style
- node An element node - - read-only
- className The class name of the element - - read/write
- currentStyle A style object that gives the cascaded and inherited styles applied to the element - - read-only - - IE compatible browsers only - For cross-browser scripting, see also - window.getComputedStyle
- styleName Text representation of the named style value - - read/only
- runtimeStyle A style object that gives the computed styles applied to the element - Frequently gives useless values or empty strings - - read-only - - IE only - For cross-browser scripting, see also - window.getComputedStyle
- styleName Text representation of the named style value - - read/only
- style An object containing all inline styles of the element - - read-only
- styleName Text representation of the named style value - - read/write
- window The window object for the current document - - read-only
- getComputedStyle(nodeReference,string: pseudoElement) Returns a style object that gives the cascaded and inherited styles applied to the element - - pseudoElement parameter is either a pseudo element name, or null - - Standards compliant browsers only - For cross-browser scripting, see also - elementNode.currentStyle - elementNode.runtimeStyle
- styleName Text representation of the named style value - - read/only
- getComputedStyle(nodeReference,string: pseudoElement) Returns a style object that gives the cascaded and inherited styles applied to the element - - pseudoElement parameter is either a pseudo element name, or null - - Standards compliant browsers only - For cross-browser scripting, see also - elementNode.currentStyle - elementNode.runtimeStyle
Element node size and position (not standardised)
- node An element node - - read-only
- clientHeight The height of the element inside its border, minus any scrollbar height - - read-only
- clientWidth The width of the element inside its border, minus any scrollbar width - - read-only
- offsetHeight The height of the element outside its border - - read-only
- offsetLeft The distance between the left edge of the node and the left edge of the offsetParent node - - read-only
- offsetParent The parent element that the browser has chosen to be the offsetParent - Only reliable cross-browser if all chained offsets are added together - - read-only
- offsetTop The distance between the top edge of the node and the top edge of the offsetParent node - - read-only
- offsetWidth The width of the element outside its border - - read-only
- scrollHeight The height of the element's contents and padding - Only reliable if it has a horizontal scrollbar - - read-only
- scrollLeft The horizontal distance that the element has been scrolled - - read-write
- scrollTop The vertical distance that the element has been scrolled - - read-write
- scrollWidth The width of the element's contents and padding - Only reliable if it has a vertical scrollbar - - read-only
Table and associated nodes
In addition to the normal node functionality, nodes belonging to tables have extra functionality, specially designed to work with the layouts of tables. All elements have their standard HTML attributes available as properties (using the same casing pattern as style properties).
To walk the DOM tree
- node A table (or related) element node - - read-only
- caption The caption of the table if element is a table - - read-only
- cells[] A collection of all cells in the element if the element is a tr - - read-only
- rows[] A collection of all rows in the element if the element is a thead, tbody or tfoot - - read-only
- tBodies[] A collection of all tbodies in the table if element is a table - - read-only
- tfoot The tfoot element of the table if element is a table - - read-only
- thead The thead element of the table if element is a table - - read-only
Attaching, copying or removing nodes
- node A table (or related) element node - - read-only
- createCaption() Creates a table caption (or returns the existing one) and inserts it - into the element, if the element is a table - Returns the caption - - Does not work in IE on Mac
- createTFoot() Creates a tfoot (or returns the existing one) and inserts it - into the element, if the element is a table - Returns the tfoot - - Does not work in IE on Mac
- createTHead() Creates a thead (or returns the existing one) and inserts it - into the element, if the element is a table - Returns the thead - - Does not work in IE on Mac
- deleteCaption() Deletes the table caption if the element is a table - - Does not work in IE on Mac
- deleteCell(cellIndex) Deletes the cell at the specified index if the element is a tr
- deleteRow(rowIndex) Deletes the row at the specified index if the element is a table, thead, tbody or tfoot
- deleteTFoot() Deletes the tfoot element if the element is a table
- deleteTHead() Deletes the thead element if the element is a table
- insertRow(index) Inserts a new tr at the specified index within the element - if element is a thead, tbody or tfoot - Use -1 to append - Also available for table elements, where it attempts to add rows - in the same section as the existing row at that index (it will create - a tbody if none exists) - - Returns the new row - - Does not work in IE on Mac
- insertCell(index) Inserts a new td at the specified index within the element if element is a tr - Use -1 to append - - Returns the new cell - - read-only - - Does not work in IE on Mac
Node information
- node A table (or related) element node - - read-only
- cellIndex The index of the cell within the row, if element is a td or th
- rowIndex The index of the row within the table
- sectionRowIndex Index of the row within the thead, tbody or tfoot
Stylesheets (if supported)
- document The document object for the current document - - read-only
- styleSheets[] Collection of all stylesheets in the document - - read-only
- addRule(string selectors,string styles) Adds a new rule to the end of the stylesheet - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].insertRule
- cssRules[] Collection of all CSS rules in the stylesheet, that are not inside a @media block - includes normal style rules, @charset rules, @import rules, @font-face rules, @media blocks, @page rules - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules
- cssRules[] Gives all child rules, if the rule is a @media rule - Children as document.styleSheets[int numberOfStyleSheet].cssRules - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules
- cssText The textual representation of the rule, including the selector and styles - - read/write (read-only in Mozilla, KHTML, WebKit, and IE) - - Standards compliant browsers only
- deleteRule(int index) Deletes the CSS rule at the specified index within the media block, - if the parent rule is a @media rule - - Standards compliant browsers only
- encoding The encoding specified by the rule if the rule is a @charset rule - - read/write - - Standards compliant browsers only
- href The URL specified by the rule if the rule is an @import rule - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].imports[j].href
- insertRule(string rule,int index) Inserts a new rule at the specified index within the media block, - if the parent rule is a @media rule - If the index is the same as the cssRules.length, the new rule will be added at the end - - Standards compliant browsers only
- media Provides information about what media types the rule applies to, - if the rule is a @media or @import rule - Children as document.styleSheets[int numberOfStyleSheet].media - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].imports[j].media
- parentRule Refers to the @media rule that the current rule is contained inside (if there is one) - - read/only - - Standards compliant browsers only
- parentStyleSheet Refers to the styleSheet that the rule is inside - - read/only
- selectorText The selector part of the rule, if it is a normal style rule or a @page rule - - read/write
- style Refers to the styles in the CSS rule, if the rule is a normal - style rule, @font-face rule or @page rule - - read/only
- cssText The textual representation of the style part of the rule - - read/write
- item(int index) Returns the style at the specified index within the rule - - Standards compliant browsers only
- length Gives the number of styles that the browser sees inside the rule - - read/only - - Standards compliant browsers only
- getPropertyValue(string style-name) Returns the value of the named style - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules[j].style.nameOfStyle
- setProperty(string style-name,string styleValue,string priority) Creates or replaces the named style inside the rule, assigning it the specified value - The priority is typically an empty string or 'important' - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules[j].style.nameOfStyle
- getPropertyPriority(string style-name) Returns the priority of the named style - Typically an empty string or 'important' - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules[j].style.nameOfStyle
- removeProperty(string style-name) Removes the specified style from the rule - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].rules[j].style.nameOfStyle
- nameOfStyle Text representation of the named style value - - read/write
- styleSheet The stylesheet object of the stylesheet imported by the rule, if the rule is an @import rule - Children as document.styleSheets[int numberOfStyleSheet] - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].imports[j]
- type Number that says what type of rule this is: - 0: unknown @ rule - 1: normal style rule - 2: @charset rule - 3: @import rule - 4: @media rule - 5: @font-face rule - 6: @page rule - - read/only - For cross-browser scripting, see also - document.styleSheets[i].insertRule
- deleteRule(int index) Deletes the CSS rule at the specified index - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].removeRule
- disabled Says if the stylesheet is disabled - - read/write
- href The href of the stylesheet - - read-only
- imports[] The stylesheet object of the stylesheet imported by an @import rule - Children as document.styleSheets[int numberOfStyleSheet] - - read/only - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].cssRules[j].styleSheet
- insertRule(string rule,int index) Inserts a new rule at the specified index within the stylesheet - If the index is the same as the cssRules.length, the new rule will be added at the end - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].addRule
- media Says what media types the stylesheet applies to - - read/write - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].media.mediaText
- media Provides information about what media types the stylesheet applies to - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].media as property
- appendMedium(string mediaType) Adds the new media type to the list of media types that the stylesheet applies to - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].media as property
- deleteMedium(string mediaType) Removes the new media type from the list of media types that the stylesheet applies to - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].media as property
- item(int index) Returns the name of the media type at the specified index - - Standards compliant browsers only
- length Gives the number of media types that the stylesheet applies to - (As interpreted by the browser) - - read/only - - Standards compliant browsers only
- mediaText String representation of what media types the stylesheet applies to - - read/write - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].media as property
- ownerNode A reference to the STYLE or LINK element that creates the stylesheet - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].owningElement
- sheet References the StyleSheet object created by the STYLE or LINK element - - read/only - - Standards compliant browsers only - For cross-browser scripting, see also - document.styleSheets[i].owningElement.styleSheet
- ownerRule A reference to the @import rule that imported this stylesheet - - read/only - - Standards compliant browsers only
- owningElement A reference to the STYLE or LINK element that creates the stylesheet - - read/only - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].ownerNode
- styleSheet References the StyleSheet object created by the STYLE or LINK element - - read/only - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].ownerNode.sheet
- removeRule(int index) Deletes the CSS rule at the specified index - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].deleteRule
- rules[] Collection of all CSS normal style rules in the stylesheet - - read/only - - Internet Explorer model only - For cross-browser scripting, see also - document.styleSheets[i].cssRules
- parentStyleSheet Refers to the styleSheet that the rule is inside - - read/only
- selectorText The selector part of the rule - - read/write
- style Refers to the styles in the CSS rule - - read/only
- cssText The textual representation of the style part of the rule - - read/write
- nameOfStyle Text representation of the named style value - - read/write
- title Reflects the TITLE attribute of the STYLE or LINK element that creates the stylesheet - - read/only
- type Reflects the TYPE attribute of the STYLE or LINK element that creates the stylesheet - (Typically 'text/css') - - read/only
- styleSheets[] Collection of all stylesheets in the document - - read-only
Events
Adding and removing event listeners
Note that although the methods are attached to all nodes, browsers will only be able to detect events on element nodes, the document node, and the window object.
- node An element node, the document node, or the window object - - read-only
- addEventListener(string: event,function,bool: phase) Adds the specified function as a handler for the named event - Phase can be true for capture, or false for bubble - - Standards compliant browsers only - For cross-browser scripting, see also - node.attachEvent
- attachEvent(string: onevent,function) Adds the specified function as a handler for the named event - - IE compatible browsers only - For cross-browser scripting, see also - node.addEventListener
- detachEvent(string: onevent,function) Removes the specified function as a handler for the named event - - IE compatible browsers only - For cross-browser scripting, see also - node.removeEventListener
- removeEventListener(string: event,function,bool: phase) Removes the specified function as a handler for the named event - Phase can be true for capture, or false for bubble - - Standards compliant browsers only - For cross-browser scripting, see also - node.detachEvent
Creating event objects
- document The document object for the current document - - read-only
- createEvent(string: EventModule) Creates an uninitialised event from the specified event module - Event modules are: - - Events: non-standard events that do not fit into other categories - - HTMLEvents: 'abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload' - - KeyEvents: 'keydown', 'keypress', and 'keyup' (Mozilla only, for cross browser scripting see UIEvents) - - MouseEvents: 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', and 'mouseup' - - MutationEvents: 'DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMCharacterDataModified', 'DOMNodeInsertedIntoDocument', 'DOMNodeRemovedFromDocument', and 'DOMSubtreeModified' - - UIEvents: 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', also in non-Mozilla 'keydown', 'keypress', and 'keyup' (for cross browser scripting see KeyEvents) - - Standards compliant browsers only - For cross-browser scripting, see also - document.createEventObject
- createEventObject(optional templateObject) Creates an uninitialised event - If an existing event object is passed as a parameter, - it will be used as a template to create the new object - - IE compatible browsers only - For cross-browser scripting, see also - document.createEvent
Preparing event objects
- eventObject The event object created by document.createEvent - - read-only - - Standards compliant browsers only
- initEvent('type', bubbles, cancelable) Initialises the event as a generic event, without defining additional properties - Available for all event types - - Standards compliant browsers only
- initKeyEvent('type', bubbles, cancelable, window, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode) Initialises the event as a key event - Available for 'KeyEvents' event types - - Mozilla only
- initMouseEvent('type', bubbles, cancelable, window, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) Initialises the event as a mouse event - Available for 'MouseEvents' event types - - Standards compliant browsers only
- initMutationEvent('type', bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange) Initialises the event as a mutation event - Available for 'MutationEvents' event types - - Standards compliant browsers only
- initUIEvent('type', bubbles, cancelable, window, detail) Initialises the event as a generic UI event, without defining additional properties - Available for 'MouseEvents', 'UIEvents', and Mozilla's 'KeyEvents' event types - - Standards compliant browsers only
Firing events
- node An element node, the document node, or the window object - - read-only
- dispatchEvent(eventObject) Fires the event, with the element node as the target - - Standards compliant browsers only - For cross-browser scripting, see also - node.fireEvent
- fireEvent('ontype',eventObject) Fires the specified event, with the element node as the srcElement - - IE compatible browsers only - For cross-browser scripting, see also - node.dispatchEvent
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 The event object - - read-only
- attrChange Says the type of change for DOMAttrModified mutation events - 1: modification - 2: addition - 3: removal - - read-only - - Standards compliant browsers only
- attrName The name of the attribute for DOMAttrModified mutation events - - read-only - - Standards compliant browsers only
- bubbles Boolean value says if the event is a type that can bubble - - read-only - - Standards compliant browsers only
- cancelable Boolean value says if the event can be cancelled - - read-only - - Standards compliant browsers only
- cancelBubble Set to true to prevent the event from being processed by any more event hanler stages - - read-write - - IE compatible browsers only - For cross-browser scripting, see also - eventObject.stopPropagation
- charCode The value of the Unicode character associated with key events - - read-only - - Mozilla compatible browsers only
- currentTarget A reference to the element that is currently processing the event - - read-only - - Standards compliant browsers only
- detail Extra information depending on the event - for example a click event may give the number of consecutive clicks - - read-only - - Standards compliant browsers only
- eventPhase Says what phase of the event is currently being processed - 1: capture phase - 2: bubble phase on the target element itself - 3: during the bubbling phase on the target's ancestors - 0: for a manually created event object that has not yet been fired - - read-only - - Standards compliant browsers only
- fromElement Gives the previous element the mouse was over for mouseover events - - read-only - - IE compatible browsers only - For cross-browser scripting, see also - eventObject.relatedTarget
- metaKey Boolean value says if the system meta key is pressed - For Windows this is the window key - For Mac this is the apple/command key - Other systems may have their own meta keys - - read-only - - Standards compliant browsers only
- newValue The new nodeValue after a mutation event - - read-only - - Standards compliant browsers only
- preventDefault() Prevents the default action from occuring after event processing is complete - - Standards compliant browsers only - For cross-browser scripting, see also - eventObject.returnValue
- prevValue The previous nodeValue before a mutation event - - read-only - - Standards compliant browsers only
- relatedNode Performs a similar function to relatedTarget, for mutation events - - read-only - - Standards compliant browsers only
- relatedTarget A reference to the related element for relevant events - such as the element the mouse used to be over before a mouseover event - - read-only - - Standards compliant browsers only - For cross-browser scripting, see also - eventObject.fromElement - eventObject.toElement
- returnValue Set to false to prevent the default action from occuring after event processing is complete - - read-write - - IE compatible browsers only - For cross-browser scripting, see also - eventObject.preventDefault
- stopPropagation() Prevents the event being processed by any - more handlers at further stages in the event flow - - read-only - - Standards compliant browsers only - For cross-browser scripting, see also - eventObject.cancelBubble
- toElement Gives the element the mouse is about to move over for mouseout events - - read-only - - IE compatible browsers only - For cross-browser scripting, see also - eventObject.relatedTarget
Last modified: 11 May 2011