In this chapter, we'll discuss the W3C DOM, a standard document object model defined by the World Wide Web Consortium and implemented (at least partially) by Netscape 6 and Internet Explorer 5 and 6. This DOM standard is a full-featured superset of the traditional web-browser DOM. It represents HTML (and XML) documents in a tree structure and defines properties and methods for traversing the tree and examining and modifying its nodes. Other portions of the standard specify techniques for defining event handlers for the nodes of a document, working with the style sheets of a document, and manipulating contiguous ranges of a document.
Technically, the W3C issues "recommendations." These recommendations serve the same purpose and carry the same weight as international standards do, however, and are called "standards" in this book.
This chapter begins with an overview of the DOM standard and then describes the core portions of the standard for working with HTML documents. The discussion of the core standard is followed by short sections that explain the DOM-like features of Internet Explorer 4 and Netscape 4. The chapter ends with an overview of two optional parts of the DOM standard that are closely related to the core. Later chapters cover advanced DOM features for working with style sheets and events.
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.
<p>This is a <i>simple</i> document.
The DOM representation of this document is the tree pictured in Figure 17-1.
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.
The DOM tree structure illustrated in Figure 17-1 is represented as a tree of various types of Node objects. The Node interface 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.
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.
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.
The Node at the root of the DOM tree is a Document object. The 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.
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.
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.
All other HTML tags have corresponding interfaces defined by the HTML 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."
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.
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.
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. 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.
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.
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.
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.
In Internet Explorer 6 (on Windows), hasFeature( ) 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.
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.
ELEMENT_NODE: 1,    // with the following properties and values.
ATTRIBUTE_NODE: 2,  // Note that these are HTML node types only.
COMMENT_NODE: 8,    // other constants here.
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;            // But this way is easier!
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.
var t = new Text("this is a new text node");  // No such constructor!
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.