Rough Guide To The DOM (part 1)

Find out how the new W3C DOM finally brings some standards to the decidedly non-standard world of the Web.

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

<script language="JavaScript">
document.all.a.style.color = "red";
</script>

Here's the code I would use in Mozilla:

<script language="JavaScript">
var obj = document.childNodes[0].childNodes[1].childNodes[0];
obj.style.color = "red";
</script>

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:

  1. start at the top ("document");

  2. move down to the main branch - the <html> tag, or "document.childNodes[0]";

  3. then to the second sub-branch - the <body> tag or "document.childNodes[0].childNodes[1]";

  4. then to the <div> - "document.childNodes[0].childNodes[1].childNodes[0]";

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

<script language="JavaScript">
var obj = document.childNodes[0].childNodes[1].childNodes[0];
alert(obj.nodeName);
obj.style.color = "red";
</script>

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

document.childNodes[0].childNodes[1].firstChild

document.childNodes[0].firstChild.nextSibling.firstChild

document.childNodes[0].childNodes[1].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.

<script language="JavaScript">
var obj = document.getElementById("a");
obj.style.color = "red";
</script>

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.

<html><head></head><body id="body" bgcolor="white"><font face="Arial" size="2">This stuff is giving me a headache already!</font>

<script language="JavaScript">

// get to the <font> tag
var fontObj = document.getElementById("body").childNodes[0];

// check the tag - returns "FONT"
// alert(fontObj.nodeName);

// check the type of node - returns 1
// alert(fontObj.nodeType);

// get the text within the <font> tag
var textObj = fontObj.childNodes[0];

// check the text value - returns "This stuff is giving me a headache already!"
// alert(textObj.data);

// check the type of node - returns 3
// alert(textObj.nodeType);

</script>

</body>
</html>

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

document.getElementsByTagName("form");

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

<script language="JavaScript">
// get a list of all <div> tags
var divCollection = document.getElementsByTagName("div");

// get a reference to the second <div> in the collection
var deweyObj = divCollection[1];

// verify that we are where we think we are
// alert(deweyObj.getAttribute("id"));

// change the text string within the <div>
deweyObj.childNodes[0].data = "Dewey rocks!";
</script>

A collection of all the tags within a document (a lot like "document.all") can be obtained with

document.getElementsByTagName("*");

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.

<html><head></head><body id="body" bgcolor="white"><font face="Arial" size="2">This stuff is giving me a headache already!</font><br>Click to <a href="javascript:increaseFontSize();">increase font size</a> or <a href="javascript:changeText()">change text string</a>

<script language="JavaScript">

// get to the <font> tag
var fontObj = document.getElementById("body").childNodes[0];

// check the tag - returns "FONT"
// alert(fontObj.nodeName);

// check the type of node - returns 1
// alert(fontObj.nodeType);

// get the text within the <font> tag
var textObj = fontObj.childNodes[0];

// check the text value - returns "This stuff is giving me a headache already!"
// alert(textObj.data);

// check the type of node - returs 3
// alert(textObj.nodeType);

function changeText()
{
// alter the node value
textObj.data = "I need some aspirin. Now.";
}

function increaseFontSize()
{
// get the value of the "size" attribute of the node
var size = fontObj.getAttribute("size");
// increase by 1
size += 1;
// set the new value
fontObj.setAttribute("size", size);
}
</script>

</body>
</html>

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

Alternatives

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.

<html><head></head><body id="body" bgcolor="white"><font face="Arial" size="2">This stuff is giving me a headache already!</font>

<script language="JavaScript">

// get to the <font> tag
var fontObj = document.getElementById("body").childNodes[0];

// return the number of attributes of the <font> tag
// or the length of the attributes[] collection
// returns 2
// alert(fontObj.attributes.length);

// returns the name of the first attribute - "face"
// alert(fontObj.attributes[0].name);

// returns the value of the first attribute - "Arial"
// alert(fontObj.attributes[0].value);

// changes the value of the first attribute to "Verdana"
fontObj.attributes[0].value = "Verdana";

// returns the new value of the first attribute - "Verdana"
// alert(fontObj.attributes[0].value);

</script>

</body>
</html>

Shazam!

The DOM also allows you to modify CSS properties of specific HTML tags - as the following example demonstrates:

<html>
<head>
<script language="JavaScript">

function disappear()
{
var obj = document.getElementById("mirage");
obj.style.display = "none";
}

</script>

</head>
<body>

<div id="mirage">
Now you see it...
</div>
<a href="javascript:disappear()">...now you don't!</a>
</body>
</html>

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:

<html>
<head>
<script language="JavaScript">

function transform()
{
var obj = document.getElementById("marvel");
obj.style.fontFamily = "Verdana";
obj.style.fontSize = "40pt";
obj.style.backgroundColor = "red";
obj.style.color = "black";
obj.style.textDecoration = "underline";
obj.style.textAlign = "center";
obj.style.letterSpacing = "10";
}

</script>

</head>
<body>

<div id="marvel">
Captain Marvel
</div>
<a href="javascript:transform()">shazam!</a>
</body>
</html>

That's about it for the moment. In the second part of this article, I'll be running through some simple code examples for simple JavaScript applications - image swaps, form validation and frame navigation - using the new DOM structures. I'll also be discussing the appendChild() and createNode() functions, which allow developers to add new elements to the document tree through program code. Don't miss it!

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 on30 Mar 2001.