17.1 An Overview of the DOM
The DOM API is not particularly complicated, but before we can begin
our discussion of programming with the DOM, there are a number of
things you should understand about the DOM architecture.
17.1.1 Representing Documents as Trees
HTML documents have
a hierarchical structure that is
represented in the DOM as a tree structure. The nodes of the tree
represent the various types of content in a document. The tree
representation of an HTML document primarily contains nodes
representing elements or tags such as <body>
and <p> and nodes representing strings of
text. An HTML document may also contain nodes representing HTML
comments.[2] Consider the following simple HTML
document:
[2] The DOM can also be used to represent
XML documents, which have a more
complex syntax than HTML documents, and the tree representation of
such a document may contain nodes that represent XML entity
references, processing instructions, CDATA sections, and so on. Most
client-side JavaScript programmers do not need to use the DOM with
XML documents, and although the XML-specific features of the DOM are
covered in the DOM reference section, they are not emphasized in this
chapter.<html>
<head>
<title>Sample Document</title>
</head>
<body>
<h1>An HTML Document</h1>
<p>This is a <i>simple</i> document.
</body>
</html>
The DOM representation of this document is the tree pictured in Figure 17-1.
Figure 17-1. The tree representation of an HTML document

If you are not already familiar with tree structures in computer
programming, it is helpful to know that they borrow terminology from
family trees. The node directly above a node is the
parent of that node. The nodes one level
directly below another node are the children of
that node. Nodes at the same level, and with the same parent, are
siblings . The set of nodes any number of levels
below another node are the descendants of that
node. And the parent, grandparent, and all other nodes above a node
are the ancestors of that node.
17.1.2 Nodes
The
DOM tree structure
illustrated in Figure 17-1 is represented as a tree
of various types of Node objects. The Node interface[3] defines properties and methods
for traversing and manipulating the tree. The
childNodes property of a Node object returns a
list of children of the node, and the
firstChild
, lastChild,
nextSibling, previousSibling,
and parentNode properties provide a way to
traverse the tree of nodes. Methods such as appendChild(
) , removeChild( ),
replaceChild( ), and insertBefore(
) enable you to add and remove nodes from the document
tree. We'll see examples of the use of these properties and
methods later in this chapter.
[3] The DOM standard defines interfaces, not classes. If you are not
familiar with the term interface in
object-oriented programming, you can think of it as an abstract kind
of class. We'll describe the difference in more detail later in
this DOM overview.
17.1.2.1 Types of nodes
Different types of nodes in the document tree are represented by
specific subinterfaces of Node. Every Node object has a
nodeType property that specifies what kind of
node it is. If the nodeType property of a node
equals the constant Node.ELEMENT_NODE, for
example, you know the Node object is also an
Element
object and you can use all the methods and properties defined by the
Element interface with it. Table 17-1 lists the
node types commonly encountered in HTML documents and the
nodeType value for each one.
Interface | nodeType constant | nodeType value |
---|---|---|
Element | Node.ELEMENT_NODE | 1 |
Text | Node.TEXT_NODE | 3 |
Document | Node.DOCUMENT_NODE | 9 |
Comment | Node.COMMENT_NODE | 8 |
DocumentFragment | Node.DOCUMENT_FRAGMENT_NODE | 11 |
Attr | Node.ATTRIBUTE_NODE | 2 |
documentElement property of this object refers to an
Element object that represents the root element of the document. For
HTML documents, this is the <html> tag that
is either explicit or implicit in the document. (The Document node
may have other children, such as Comment nodes, in addition to the
root element.) The bulk of a DOM tree consists of Element objects,
which represent tags such as <html> and
<i>, and Text objects, which represent strings of
text. If the document parser preserves comments, those comments are
represented in the DOM tree by Comment objects. Figure 17-2 shows a partial class hierarchy for these and
other core DOM interfaces.
Figure 17-2. A partial class hierarchy of the core DOM API

17.1.2.2 Attributes
The
attributes
of an element (such as the src and
width attributes of an
<img> tag) may be queried, set, and deleted
using the getAttribute(
) , setAttribute( ), and
removeAttribute( ) methods of the Element
interface.
Another, more awkward way to work with attributes is with the
getAttributeNode(
) method, which returns an
Attr object
representing an attribute and its value. (One reason to use this more
awkward technique is that the Attr interface defines a
specified property that allows you to determine
whether the attribute is literally specified in the document, or
whether its value is a default value.) The Attr interface appears in
Figure 17-2, and it is a type of node. Note,
however, that Attr objects do not appear in the
childNodes[] array of an element and are not
directly part of the document tree in the way that Element and Text
nodes are. The DOM specification allows Attr nodes to be accessed
through the
attributes[] array of the Node interface, but
Microsoft's Internet Explorer defines a different and
incompatible attributes[] array that makes it
impossible to use this feature portably.
17.1.3 The DOM HTML API
The DOM standard was designed for use with both XML and HTML
documents. The core
DOM API -- the Node, Element,
Document, and other interfaces -- are relatively generic
and apply to both types of documents. The DOM standard also includes
interfaces that are specific to HTML documents. As you can see from
Figure 17-2, HTMLDocument is an HTML-specific
subinterface of Document, and HTMLElement is an HTML-specific
subinterface of Element. Furthermore, the DOM defines tag-specific
interfaces for many HTML elements. These tag-specific interfaces,
such as HTMLBodyElement and HTMLTitleElement, typically define a set
of properties that mirror the HTML tag's attributes.
The HTMLDocument interface defines various document properties and
methods that were supported by browsers prior to W3C standardization.
These include the
location property, forms[]
array, and write( ) method, which are described in
Chapter 13, Chapter 14,
and Chapter 15.
The
HTMLElement interface defines
id, style,
title, lang,
dir , and className
properties. These properties allow convenient access to the values of
the id, style,
title, lang,
dir, and class attributes,
which are allowed on all HTML tags. A number of HTML tags, listed in
Table 17-2, accept no attributes other than these
six, and so are fully represented by the HTMLElement interface.
<abbr> | <acronym> | <address> | <b> | <bdo> |
<big> | <center> | <cite> | <code> | <dd> |
<dfn> | <dt> | <em> | <i> | <kbd> |
<noframes> | <noscript> | <s> | <samp> | <small> |
<span> | <strike> | <strong> | <sub> | <sup> |
<tt> | <u> | <var> |
portion of the DOM specification. For many HTML tags, these
interfaces do nothing more than provide a set of properties that
mirror their HTML attributes. For example, the
<ul> tag has a corresponding
HTMLUListElement interface, and the <body>
tag has a corresponding HTMLBodyElement interface. Because these
interfaces simply define properties that are standardized by the HTML
standard, they are not documented in detail in this book. You can
safely assume that the HTMLElement object that represents a
particular HTML tag has properties for each of the standard
attributes for that tag (but see the naming conventions described in
the next section). Note that the DOM standard defines properties for
HTML attributes as a "convenience" to script writers. The
general (and possibly preferred) way to query and set attribute
values is with the getAttribute( ) and
setAttribute( ) methods of the Element object.
Some of the interfaces defined in the HTML DOM define additional
properties or methods, other than those that mirror HTML attribute
values. For example, the HTMLInputElement interface defines
focus( ) and blur( ) methods,
and the HTMLFormElement interface defines
submit( ) and reset( ) methods
and a length property. Methods and properties like
these typically predate DOM standardization and have been made part
of the DOM standard for backward compatibility with existing
practice. Interfaces like these are documented in the DOM reference
section. You can usually also find information about the
"existing practice" portions of these interfaces in the
client-side reference section, although this information is typically
referenced under a name that also predates DOM standardization; for
example, you can find information about HTMLFormElement and
HTMLInputElement in the client-side reference section under
"Form" and "Input."
17.1.3.1 HTML naming conventions
When working with the HTML-specific portions of the
DOM
standard, you should be aware of some simple naming conventions.
Properties of the HTML-specific
interfaces begin with lowercase letters. If the property name
consists of multiple words, the first letters of the second and
subsequent words are capitalized. Thus, the
maxlength attribute of the
<input> tag translates into the
maxLength property of HTMLInputElement.
When an HTML
attribute name conflicts
with a JavaScript keyword, it is prefixed with the string
"html" to avoid the conflict. Thus, the
for attribute of the
<label> tag translates to the
htmlFor property of the HTMLLabelElement. An
exception to this rule is the
class
attribute (which can be specified for any HTML element); it
translates to the
className[4]
[4] The name className is misleading, because in
addition to specifying a single class name, this property (and the
HTML attribute it represents) can also specify a space-separated list
of class names.
17.1.4 DOM Levels and Features
There are two versions, or "levels," of the
DOM standard. DOM Level 1 was
standardized in October, 1998. It defines the core DOM interfaces,
such as Node, Element, Attr, and Document, and also defines various
HTML-specific interfaces. DOM Level 2 was standardized in November,
2000.[5] In addition to some updates to the core interfaces, this
new version of the DOM is greatly expanded to define standard APIs
for working with document events and CSS style sheets and to provide
additional tools for working with ranges of documents. As of this
writing, the DOM Working Group at the W3C is working to standardize
DOM Level 3. You may also sometimes see a reference to DOM Level 0.
This term does not refer to any formal standard but is used to refer
informally to the common features of the HTML document object models
implemented by Netscape and Internet Explorer prior to W3C
standardization.
[5] Except for the HTML-specific portions of the
standard, which are still at the "working draft" stage as
of November 2001. Fortunately, the current working draft is presumed
stable and includes only minor changes (documented in this book) from
the HTML-specific portions of the Level 1 standard. As of Level 2, the DOM standard has been "modularized."
The core module, which defines the basic tree structure of a document
with the Document, Node, Element, and Text interfaces (among others),
is the only required module. All other modules are optional and may
or may not be supported, depending on the needs of the
implementation. The DOM implementation of a web browser would
obviously support the HTML module, since web documents are written in
HTML. Browsers that support CSS style sheets typically support the
StyleSheets and CSS modules, because (as we'll see in Chapter 18) CSS styles play a crucial role in Dynamic
HTML programming. Similarly, since almost all interesting client-side
JavaScript programming requires event-handling capabilities, you
would expect web browsers to support the Events module of the DOM
specification. Unfortunately, the Events module was only recently
defined by the DOM Level 2 specification and is not yet widely
supported at the time of this writing. We'll see a complete
list of DOM Level 2 modules in the next section.
17.1.5 DOM Conformance
At the time of this writing, no
browser is completely
conformant to the DOM standard. Recent releases of
Mozilla come closest, and
complete DOM Level 2 conformance is a goal of the Mozilla project.
Netscape 6.1 does a good job of conforming
to the most important Level 2 modules, and Netscape 6.0 does an
adequate job but has gaps in its coverage. Internet Explorer 6 is
mostly compliant (with at least one annoying exception) with the
Level 1 DOM, but does not support many of the Level 2 modules --
most notably the Events module, which is the topic of Chapter 19. Internet Explorer 5
and 5.5 have substantial gaps in their conformance but support key
DOM Level 1 methods well enough to run most of the examples in this
chapter. The Macintosh version of IE 5 has considerably better
support for the DOM than the Windows version of IE 5.
In addition to Mozilla, Netscape, and Internet Explorer, several
other browsers offer at least partial support for the DOM. The number
of available browsers has become too large, and the rate of change in
the area of standards support has grown too fast, for this book to
even attempt to provide definitive statements about which browsers
support which particular DOM features. Therefore, you'll have
to rely on other information sources to determine exactly how
conformant the DOM implementation in any particular web browser is.
One
source for conformance information is the
implementation itself. In a conformant implementation, the
implementation property of the Document object refers
to a DOMImplementation object that defines a
method named hasFeature(
). You can use this method (if it exists)
to ask an implementation whether it supports a specific feature (or
module) of the DOM standard. For example, to determine whether the
DOM implementation in a web browser supports the basic DOM Level 1
interfaces for working with HTML documents, you could use the
following code:
if (document.implementation &&
document.implementation.hasFeature &&
document.implementation.hasFeature("html", "1.0")) {
// The browser claims to support Level 1 Core and HTML interfaces
}
The hasFeature( ) method takes two arguments: the
first is the name of the feature to check, and the second is a
version number, expressed as a string. It returns
true if the specified version of the specified
feature is supported. Table 17-3 lists the feature
name/version number pairs that are defined by the DOM Level 1 and
Level 2 standards. Note that the feature names are case-insensitive:
you can capitalize them any way you choose. The fourth column of the
table specifies what other features are required for support of a
feature and are therefore implied by a return value of
true. For example, if hasFeature(
) indicates that the MouseEvents module is supported, this
implies that UIEvents is also supported, which in turn implies that
the Events, Views, and Core modules are supported.
Feature name | Version | Description | Implies |
---|---|---|---|
HTML | 1.0 | Level 1 Core and HTML interfaces | |
XML | 1.0 | Level 1 Core and XML interfaces | |
Core | 2.0 | Level 2 Core interfaces | |
HTML | 2.0 | Level 2 HTML interfaces | Core |
XML | 2.0 | Level 2 XML-specific interfaces | Core |
Views | 2.0 | AbstractView interface | Core |
StyleSheets | 2.0 | Generic style-sheet traversal | Core |
CSS | 2.0 | CSS styles | Core, Views |
CSS2 | 2.0 | CSS2Properties interface | CSS |
Events | 2.0 | Event-handling infrastructure | Core |
UIEvents | 2.0 | User-interface events (plus Events and Views) | Events, Views |
MouseEvents | 2.0 | Mouse events | UIEvents |
HTMLEvents | 2.0 | HTML events | Events |
MutationEvents | 2.0 | Document mutation events | Events |
Range | 2.0 | Document range interfaces | Core |
Traversal | 2.0 | Document traversal interfaces | Core |
returns true only for the feature HTML and Version
1.0. It does not report compliance to any of the other features
listed in Table 17-3 (although, as we'll see
in Chapter 18, it supports the most common uses of
the CSS2 module.) In Netscape 6.1, hasFeature( )
returns true for most feature names and version
numbers, with the notable exceptions of the Traversal and
MutationEvents features. It returns false for the
Core and CSS2 features with Version 2.0, indicating incomplete
support (even though support for these features is quite good).
This book documents the interfaces that make up all of the DOM
modules listed in Table 17-3. The Core, HTML,
Traversal, and Range modules are covered in this chapter. The
StyleSheets, CSS, and CSS2 modules are covered in Chapter 18, and the various Event modules (except
MutationEvents) are covered in Chapter 19. The DOM
reference section includes complete coverage of all modules.
The hasFeature( ) method is not always perfectly
reliable. As previously noted, IE 6 reports Level 1 compliance to
HTML features even though there are some problems with its
compliance. On the other hand, Netscape 6.1 reports noncompliance to
the Level 2 Core feature even though it is mostly compliant. In both
cases, you need more detailed information about exactly what is and
is not compliant. This is exactly the type of information that is too
voluminous and volatile to include in a printed book.
If you are an active web developer, you undoubtedly already know or
will discover many browser-specific support details on your own.
There are also resources on the Web that can help you. Most
importantly, the W3C (in collaboration with the U.S. National
Institute of Standards and Technology) is working on developing an
open source test suite for DOM implementations. At the time of this
writing, the test suite effort is just getting off the ground, but it
ought to prove to be an invaluable resource for fine-grained
compliance testing of DOM implementations. See http://www.w3c.org/DOM/Test/ for details.
The Mozilla organization
has a set of test suites for a
variety of standards, including DOM Level 1 (available athttp://www.mozilla.org/quality/browser_scl). Netscape
has published a test suite that includes some DOM Level 2 tests
(available athttp://developer.netscape.com/evangelism/tools/testsuites/
). Netscape has also published a partisan (and dated) comparison of
DOM compliance of an early Mozilla release versus IE 5.5 (available
at http://home.netscape.com/browsers/future/standardsl). Finally, you can also find compatibility and
compliance information at independent sites on the Web. One notable
site is published by Peter-Paul Koch. You can find a link to his DOM
Compatibility Table from his main JavaScript page (http://www.xs4all.nl/~ppk/js/).
17.1.5.1 DOM conformance in Internet Explorer
Because
IE is the most widely used web
browser, a few special notes about its compliance to the DOM
specifications are appropriate here. IE 5 and later versions support
the Level 1 Core and HTML features well enough to run the examples in
this chapter, and they support the key Level 2 CSS features well
enough to run most of the examples in Chapter 18.
Unfortunately, IE 5, 5.5, and 6 do not support the DOM Level 2 Events
module, even though Microsoft participated in the definition of this
module and had ample time to implement it for IE 6. As we'll
see in Chapter 19, event handling is crucial for
client-side event handling, and IE's lack of support for the
standard event model impedes the development of advanced client-side
web applications.
Although IE 6 claims (through its hasFeature( )
method) to support the Core and HTML interfaces of the DOM Level 1
standard, this support is actually incomplete. The most egregious
problem, and the one you are most likely to encounter, is a minor but
annoying one: IE does not support the node-type constants defined by
the Node interface. Recall that each node in a document has a
nodeType property that specifies what type of node
it is. The DOM specification also says that the Node interface
defines constants that represent each of the defined node types. For
example, the constant Node.ELEMENT_NODE represents
an Element node. In IE (at least as high as version 6), these
constants simply do not exist.
The examples in this chapter have been modified to work around this
problem by using integer literals instead of the corresponding
symbolic constants. For example, you'll see code like this:
if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) // Check if n is an Element
It is good programming style to use constants instead of hardcoded
integer literals in your code, and if you'd like to do this
portably, you can include the following code in your programs to
define these constants if they are missing:
if (!window.Node) {
var Node = { // If there is no Node object, define one
ELEMENT_NODE: 1, // with the following properties and values.
ATTRIBUTE_NODE: 2, // Note that these are HTML node types only.
TEXT_NODE: 3, // For XML-specific nodes, you need to add
COMMENT_NODE: 8, // other constants here.
DOCUMENT_NODE: 9,
DOCUMENT_FRAGMENT_NODE: 11
}
}
17.1.6 Language-Independent DOM Interfaces
Although the
DOM
standard grew out of a desire to have a common API for dynamic HTML
programming, the DOM is not of interest only to web scripters. In
fact, the standard is currently most heavily used by server-side Java
and C++ programs that parse and manipulate XML documents. Because of
its many uses, the DOM standard is defined to be
language-independent. This book describes only the JavaScript binding
of the DOM API, but you should be aware of a few other points. First,
note that object
properties in the JavaScript
binding are typically mapped to pairs of get/set methods in other
language bindings. Thus, when a Java programmer asks you about the
getFirstChild(
) method of the Node interface, you need
to understand that the JavaScript binding of the Node API
doesn't define a getFirstChild( ) method.
Instead, it simply defines a firstChild property,
and reading the value of this property in JavaScript is equal to
calling getFirstChild( ) in Java.
Another important feature of the JavaScript
binding of the DOM API is that certain DOM objects behave like
JavaScript arrays. If an interface defines a method named
item( ), objects that implement that interface
behave like read-only numerical arrays. For example, suppose
you've obtained a NodeList object by reading the
childNodes property of a node. You can obtain the
individual Node objects in the list by passing the desired node
number to the item( ) method, or, more simply, you
can simply treat the NodeList object as an array and index it
directly. The following code illustrates these two options:
var n = document.documentElement; // This is a Node object.
var children = n.childNodes; // This is a NodeList object.
var head = children.item(0); // Here is one way to use a NodeList.
var body = children[1]; // But this way is easier!
Similarly, if a DOM object has a namedItem( )
method, passing a string to this method is the same as using the
string as an array index for the object. For example, the following
lines of code are all equivalent ways to access a form element:
var f = document.forms.namedItem("myform");
var g = document.forms["myform"];
var h = document.forms.myform;
Because the DOM standard may be used in a variety of ways, the
architects of the standard were careful to define the DOM API in a
way that would not restrict the ability of others to implement the
API as they saw fit. Specifically, the DOM standard defines
interfaces instead of classes. In object-oriented programming, a
class is a fixed data type that must be implemented exactly as
specified. An interface, on the other hand, is a collection of
methods and properties that must be implemented together. Thus, an
implementation of the DOM is free to define whatever classes it sees
fit, but those classes must define the methods and properties of the
various DOM interfaces.
This architecture has a couple of important implications. First, the
class names used in an implementation might not correspond directly
to the interface names used in the DOM standard (and in this book).
Second, a single class may implement more than one interface. For
example, consider the Document object. This object is an instance of
some class defined by the web browser implementation. We don't
know what the specific class is, but we do know that it implements
the Document interface; that is, all methods and properties defined
by Document are available to us through the Document object. Since
web browsers work with HTML documents, we also know that the Document
object implements the HTMLDocument interface and that all methods and
properties defined by that interface are available to us as well.
Furthermore, if a web browser supports CSS style sheets and
implements the DOM CSS module, the Document object also implements
the DocumentStyle and DocumentCSS DOM interfaces. And if the web
browser supports the Events and Views modules, Document implements
the DocumentEvent and DocumentView interfaces as well.
Because the DOM is broken into independent modules, it defines a
number of minor add-on interfaces, such as DocumentStyle,
DocumentEvent, and DocumentView, that define only one or two methods
each. Interfaces such as these are never implemented independently of
the core Document interface, and for this reason, I do not document
them independently. When you look up the Document interface in the
DOM reference section, you'll find that it also lists the
methods and properties of its various add-on interfaces. Similarly,
if you look up one of the add-on interfaces, you'll simply find
a cross-reference to the core interface with which it is associated.
The exception to this rule is when the add-on interface is a complex
one. For example, the HTMLDocument interface is always implemented by
the same object that implements the Document object, but because it
adds substantial new functionality, I have given it a reference page
of its own.
Another important fact you need to understand is that since the DOM
standard defines interfaces instead of classes, it does not define
any constructor methods. If you want to create a new Text object to
insert into a document, for example, you cannot simply say:
var t = new Text("this is a new text node"); // No such constructor!
Since it cannot define constructors, the DOM standard instead defines
a number of useful factory
methods
for creating objects in
the Document interface. So, to create a new Text node for a document,
you would write the following:
var t = document.createTextNode("this is a new text node");
Factory methods defined by the DOM have names that begin with the
word "create". In addition to the factory methods defined
by Document, a few others are defined by DOMImplementation and
available as document.implementation.