The Current State Of Denmark
In Shakespeare's "Hamlet", one of the characters famously remarks, "Something's rotten in the state of Denmark". And each time I sit down to code some dHTML, I'm assailed by a sense of wonder at his perspicuity. That comment, laden with an undertone of doom, is such a perfect appraisal of the numerous incompatibilities between the two major browsers, and the problems they cause for developers on a daily basis, that it's hard not to laugh. And I would...if I wasn't already weeping buckets.
These incompatibilities are particularly glaring in an area known as the DOM, or Document Object Model, a standard method of accessing each and every element in the document, together with its attributes. When a primitive version of the DOM was first introduced, developers immediately realized how useful it could be in adding new levels of interactivity to a static Web page. However, as the two major browsers branched out in different directions, developing DOM constructs that were mutually incompatible, that elation quickly turned to disappointment - after all, no developer likes writing different versions of the same script for different browsers.
Of course, all is not lost. Efforts have been under way, most noticeably at the W3C [http://www.w3.org/] to establish common standards for all browsers. The release of the CSS specification, and then of the DOM Level 0 and Level 1 specifications, has resulted in most of the major browsers falling in line with the proposed standards. The flip side: since a standard is now available, browser makers will soon stop supporting their previous DOM versions...which means that all the code you wrote and the clever workarounds you devised will no longer work in newer versions of the browsers.
You can already see this happening - code written specifically for Netscape 4.x no longer works in Netscape 6.x, which is built on the Mozilla engine - and so, every developer needs to understand the new DOM standard and its impact on dHTML code development.
Over the next few pages, I'll be illustrating some of the new DOM constructs, together with examples of how they can be used in "real" HTML documents. My trusty steed in this journey will be Mozilla, the wonderful open-source browser available at http://www.mozilla.org/, which claims to be the most standards-compatible browser currently available.
Before we start, a few disclaimers.
One, this tutorial is not meant to be an exhaustive reference to the DOM - you can buy a book for that. It is merely a guide to help you in making the transition to the new object model.
Second, I don't claim to be an expert on the DOM, and much of the material in this tutorial is based on my own experience as a developer.
Finally, as new DOM standards are proposed and disposed, the material here may become invalid; you should always refer to the most current standard or recommendation at http://www.w3.org/DOM/ for up-to-date information (this is one of my favourite documents - I use it frequently when I have trouble sleeping.)
With the formalities out of the way, let's get started.
Back To Basics
We'll start with the basics - a very simple HTML page.
<html><head></head><body bgcolor="white"><div id="a" style="font-family: Arial; color: white; background: black">Wassup?</div></body></html>
Let's alter the font colour of the text within the
<div>. In Internet Explorer, this would typically be accomplished with
Here's the code I would use in Mozilla:
An explanation is in order here. Under the new DOM, every element in an HTML document is part of a "tree", and you can access each and every element by navigating through the tree "branches" until you reach the corresponding "node". Given that, here's my representation of the HTML document above, in "tree" form.
document | -- <html> | -- <head> | -- <body> | -- <div>
Now, to get to the
<div>, I need to:
start at the top ("document");
move down to the main branch - the
<html>tag, or "document.childNodes";
then to the second sub-branch - the
<body>tag or "document.childNodes.childNodes";
then to the
At this point, I have successfully navigated my way to the
<div> element in the document tree. A quick way of verifying this is to use an alert() on the object
which displays the name of the tag - DIV - in an alert box.
At this point, I can begin futzing with object attributes - in the example above, I've altered the "color" style attribute. Don't worry about this for the moment; simply verify that you have understood the manner in which I navigated through the document tree to reach the DIV.
Navigating The Family Tree
In addition to the various childNodes, the DOM also offers a number of other objects/properties which can simplify navigation between document elements.
firstChild - a reference to the first child node in the collection
lastChild - a reference to the last child node in the collection
parentNode - a reference to the node one level up in the tree
nextSibling - a reference to the next node in the current collection
previousSibling - a reference to the previous node in the current collection
And so, with reference to the example above, I could use any of the alternative routes below to navigate to the
document.childNodes.childNodes.firstChild document.childNodes.firstChild.nextSibling.firstChild document.childNodes.childNodes.firstChild.firstChild.parentNode
Each child in the tree can be either an HTML tag or a "text node". This brings up an important point - blank spaces and carriage returns between the various tags can affect the document tree structure, creating text nodes in the tree structure and causing much gnashing of teeth as you adjust your code to the new tree.
What's In A Name?
It's precisely for this reason that the DOM offers a faster and more efficient method of accessing elements within the page - the getElementById() method.
I've rewritten the example above to demonstrate how this method can be used.
As you can see, this is much simpler to read...and code.
Every node has some basic properties which come in handy for the developer - for example, the "nodeName" property returns the tag name, while the "nodeType" property returns a number indicating the type of node (HTML tag=1; HTML tag attribute=2; text block=3). If the node happens to be a text node rather than a tag, the "data" and "nodeValue" properties return the text string.
The following example demonstrates how the various node properties can be accessed - uncomment the various alert() method calls to display the various object properties.
And incidentally - a text node which contains no data returns the value "#text" to the "nodeName" property - try replacing the line of text from within the
<font> tags above with a couple of blank spaces to see what I mean.
Ducks In A Row
In addition to the getElementById() method, which is typically used to obtain a reference to a specific element, the DOM also offers the getElementsByTagName() method, used to return a collection of a specific type of element. For example, the code
would return a collection, or array, containing references to all the
<form> tags in the document. Each of these references is a node, and can then be manipulated using the standard DOM methods and properties.
Consider the following document, which contains three
<div>s, each containing a line of text
<html> <head> </head> <body bgcolor="white"> <div id="huey"> Huey here! </div> <div id="dewey"> Dewey in the house! </div> <div id="louie"> Yo dude! How's it hangin'? </div> </body> </html>
and then study the code I'd use to manipulate the text within the second
A collection of all the tags within a document (a lot like "document.all") can be obtained with
Changing Things Around
Now that you know how to find your way to specific HTML elements in the document, it's time to learn how to manipulate them. Since most of this manipulation involves altering tag attributes on the fly, the DOM offers the getAttribute() and setAttribute() methods, which are designed expressly for this purpose.
Consider the following modification to the example you just saw, which uses these two methods to alter the font size and the text string.
I've used two different methods here. In order to alter the font size, I've first used the getAttribute() method to return the current value of the attribute, and then used the setAttribute() method to write a new value. However, altering the text string is simply a matter of changing the value of the text node's "data" property.
There are a couple of things to keep in mind when using getAttribute() and setAttribute(). All attribute names should be lowercase, and both names and values should be enclosed in quotes (if you omit the quotes, the values will be treated as variables). Obviously, you should only use attributes which are relevant to the tag under consideration - for example, you cannot use a setAttribute("src") on a
An alternative way of obtaining (and setting) attribute values is via the attributes collection, which is essentially an array containing a list of all the attribute-value pairs for a specific tag. I've modified the previous example to illustrate how this works - uncomment the various alert()s to see the values of the different properties.
The DOM also allows you to modify CSS properties of specific HTML tags - as the following example demonstrates:
I've done something similar in the very first example in this article - take a look at that one too, while you're at it.
Using this technique, it's possible to apply almost any inline style to an element on the page. Remember that style properties which are hyphenated - for example, "background-color" and "font-family" - need to be written as a single word with the first character after the hyphen capitalized - for example, "backgroundColor" and fontFamily". The next example should illustrate this clearly:
Note: All examples in this article have been tested on Mozilla (build 18). Examples are illustrative only, and are not meant for a production environment. YMMV!This article was first published on 30 Mar 2001.