SEARCH
INSIDE Current Issue Columns Offline Newsletters Archives
TRACKS Web Authors Designers Developers Strategists
GUIDES Style
Sheets Web
Browsers Web
Tools Ranking
System
ABOUT WebReview.com Write for
Us Advertising Staff Contact
NETWORK WebTechniques.com WEB2000 Show Site WebCoder.com
|
 |
 |
June
18, 1999 Issue > Browsers
Understanding the Document Object Model
By Mitch
Gould
The DOM
in Version 5 Browsers
The contentious experience of the W3C's DOM task force illustrates many
of the problems with designing a critical industrywide infrastructure by
committee. DOM syntax went through several major changes in less than a
year, to the consternation of web coders trying to author new pages
according to the standard. Even the most powerful companies represented on
the committee, such as Microsoft, found it hard to keep up with the
various drafts. But now that their work on DOM Level 1 is done, the result
can be found at the W3C site.
Frankly, the standards specification is not intended to be clear to
outsiders, so some interpretation is essential. Think of a document as a
fractal object, like a tree with several "nodes." In botany, a node
corresponds to a root, a trunk, a branch, or a twig, terminating in a leaf
or a nut. (The leaf and nut might correspond to a tag's attribute and
text-data in the document model. These can't have children.) Each sort of
node is an object with its own set of properties and methods.
The node is therefore the atomic unit of a document. Like a trunk or a
branch, each node can have children nodes (except terminal nodes such as
text-data), and each node has a parent node (except for the root node). By
getting a reference to the child or parent of any node, one can
recursively climb the tree and reach any part of the document.
Here's a list of nodes in XML and HTML documents.
- Node. A reference to an element, attribute, or string of
text.
- Document. An object representing the document itself.
- Element. An object representing one of the document's tags.
An element can contain another element, text, a comment, a processing
instruction, an entity reference, and text (Cdata).
- Attribute. A
name="value" pair that represents a
tag's property (for instance, src is an attribute for the
img element).
- Processing instruction. Instructions for an XML parser, such
as XML version.
- Comment. Developer comments ignored by the parser.
- Text. An object that contains the text content of an
element—in other words, actual document content.
- Cdata section. Text-content characters that do not include
markup characters.
- Document Fragment. Some portion (it could be the entire
document) of a document.
- Entity. A token to be expanded by a pre-processor into a
replacement string. An XML entity declaration lets you associate a name
with a content fragment, such as actual text, part of a document type
declaration (DTD), or a reference to an external file.
- Entity reference. When the token is prefixed with the "&"
character, a replacement string is put in its place. To insert an entity
within a document, get a reference to it with
EntityReference.
- Document type. In XML, a grammar that defines the tags and
attributes supported in an XML document constitutes a document type
definition. One can use the
DocumentType object to access a
list of entities within that document.
One useful thing about a document fragment is that it allows one to
move around large chunks of a document as a single object. Equally useful
is the fact that unlike a document, a document fragment need not be "well
formed" (that is, it need not have a perfect set of nested tags).
DHTML vs. DOM
Authors who are familiar with object models in DHTML should find the
DOM reasonably easy to use—once they master the scripts required to
navigate recursively through the document. For instance, consider a
simplified version of Dan Steinman's cross-browser DHTML function for
writing new content shown in Listing 4.
ns4 = (document.layers)? true:false
ie4 = (document.all)? true:false
function simpleLayerWrite(id,text) {
if (ns4) {
var lyr = document.layers[id].document
lyr.open()
lyr.write(text)
lyr.close()
}
else if (ie4) document.all[id].innerHTML = text
}
| Listing 4.
Writing new content in Dan Steinman's "DynLayer" object.
This DHTML function can change the content of a div tag,
given its ID value and the string for new content. Note that while the
commands are straightforward, they must take very different forms to
accomodate two incompatible object models. The ns4 flag is
true when the browser is Navigator 4 and the ie4 flag is true
when the browser is Internet Explorer 4.0. Navigator uses an object called
Layer for dynamic content, while Internet Explorer addresses the
div more directly.
By contrast, when using the DOM, there is only one set of code, for
instance, the example shown in Listing 5.
// C. DOM tree-navigation and utilities.
/*
One must climb the trunk, branches, and twigs to get
(or set!) the fruit. The recursive nature of this
algorithm reflects an essential fractal nature of
documents.
*/
function replaceAllText(startelem) {
// Climb the object tree, replacing its text nodes with
// new data.
for (var i=0; i < startelem.childNodes.length; i = i + 1) {
switch (startelem.childNodes.item(i).nodeType) {
case 1: // Element nodetype
replaceAllText(startelem.childNodes.item(i))
break;
case 3: // Text nodetype
if (datacount < model.length) {
setText(startelem.childNodes.item(i), model[datacount])
datacount = datacount + 1
} else {
setText(startelem.childNodes.item(i)," - ? - ")
}
break;
} //endswitch
} //endfor
} //endfunction
/*
Many operations on dynamic documents require one to start
from the document's body element.
*/
function getBody()
{
if(navigator.appName != "Netscape") {
resultElement = document.body;
} else {
resultElement = document.getElementsByTagName("body").item(0);
}
return resultElement;
}
// Utility function to overwrite text nodes.
function setText(tagToSet, valueToSet) {
tagToSet.nodeValue = valueToSet
}
| Listing 5.
Replacing new content in the DOM.
Listing 5 illustrates the tree-climbing algorithm that is used when
it's not feasible to locate the desired item by an ID attribute. This
function accepts a branch node and recursively travels it, replacing each
text node that it finds with the items in an array called "model." This is
a common practice that often requires one to locate the document root—its
body element, as shown in the getBody() function.
Using the DOM, an author can:
- Create elements and attach them to any point in the document tree.
- Manipulate tree branches in a document fragment and insert the
changed fragment back into the tree.
- Destroy elements.
- Move one part of the document tree to another without destroying and
recreating the content.
- Harvest the entire text content of a document.
- Search the document for a string.
- Search the tags for a given attribute.
- And do more.
Members of the model
The DOM interface exposes objects, collections, properties, and
methods. Table 1 outlines a few of the most important methods.
| Selected Document Methods |
createElement(tagName) |
creates a new tag. |
createDocumentFragment() |
creates a new DocumentFragment. |
createTextNode(data) |
creates new content for the document. |
createAttribute(name) |
creates an attribute for a tag. |
getElementsByTagName(tagname) |
returns a list of matching nodes (NodeList). |
Selected HTMLDocument Methods An HTMLDocument
object has these methods in addition to the methods of
Document. |
open() |
Opens a document for new content. |
close() |
Closes a document. |
write(text) |
Appends the text to the open document. |
writeln(text) |
Appends text and a new line to the open document. |
getElementById(elementId) |
returns the element with the given ID. |
| Selected Node Methods |
insertBefore(newChild, refChild) |
Inserts a child node into the document before the
given node. |
replaceChild(newChild, oldChild) |
Replaces the old node with the new node. |
removeChild(oldChild) |
Removes the child node from the document. |
appendChild(newChild) |
Adds the node to the document. |
hasChildNodes() |
Returns true if the node has children. |
cloneNode(deep) |
Generates a copy of the tree under the given
node. |
Selected Element Methods An Element has these
methods in addition to the methods of Node. |
getAttribute(name) |
Gets the value of the named attribute. |
setAttribute(name, value) |
Sets the value of the named attribute. |
removeAttribute(name) |
Removes the named attribute. |
getElementsByTagName(name) |
Returns a list of matching nodes
(NodeList). | Table 1.
Sample list of critical DOM methods.
Creating the view
Nodes are created using the createElement and
createTextNode methods. Manipulating nodes can, however, be
tricky because it is very easy to build invalid tree structures, and
therefore authors must familiarize themselves with the branching patterns
that occur in documents. For instance, to create a well-formed table in
the DOM, a TBODY element must be explicitly created and added
to the tree. If the TBODY element is neglected, the nodes
comprising the entire table form an invalid tree and the result is
unpredictable. This example shows how to correctly add a table.
Our example's createView() function creates table-row and
-cell elements and appends them as children to TBODY, as
shown in Listing 6.
function createView(bodyelement, themodel) {
table = document.createElement("TABLE")
table.border = 1
table.id = "viewtable"
tablebody = document.createElement("TBODY")
var modelcount = 0
for(var i=0; i < model.length; i++)
{
currentRow = document.createElement("TR")
currentCell = document.createElement("TD")
currentCell.appendChild(document.createTextNode(model[i]))
currentRow.appendChild(currentCell)
tablebody.appendChild(currentRow)
}
table.appendChild(tablebody)
bodyelement.appendChild(table)
return table
}
| Listing 6.
The createView() function creates a new table.
The cells are filled with the model data. Once the cells are assembled,
the table body is appended to the table and the completed table is
appended to the document body.
Note that elements are spawned in the document using the
createElement function, but they must be actually appended to
it with a separate method, appendChild.
The whole point of the MVC pattern is repurposability. Accordingly,
createView could be generalized to provide alternatives to a
table view, such as a select object, a tree, or even a text area. This
sample produces a one-column table. Multi-column tables are more complex.
Responding to user events
When the user clicks the button, the view requests a refresh: The table
needs to be updated.
The controller refreshes the model first, then the view. It copies the
currently selected data into the model, and then replaces the cells' text
with the contents of the model, using the utility functions in section
(C), as shown in Listing 7.
// Copy the specified dataset into the model.
function refreshModel(arraycurrent) {
// Populate the model with the current datastore.
for(var i=0; i <: arraycurrent.length; i = i + 1)
{
model[i] = arraycurrent[i]
}
return model
}
// Refresh the model first, then the view.
function refreshView(dataset) {
// Populate the model with new data.
refreshModel(dataset)
tablebody = document.getElementsByTagName("TBODY").item(0)
var count = 0
replaceAllText(tablebody)
}
| Listing 7.
The refreshModel() and refreshView() functions.
Is this a real MVC application?
Not really. It's a toy application, built for illustration. A real MVC
application would at least have a function to edit the data that appears
in the view. One point that should be stressed is that in MVC, changes to
the data must be sent to the datastore first, and only then should the
view be refreshed with the edited data, and not the other way around.
Dismantling the view
Some DOM methods allow one to easily manipulate nodes, notably the
cloneNode(), removeNode(),
replaceNode(), and swapNode() methods. These
methods provide copy, move, and delete functionality to the entire
document tree.
The example in Listing 8 shows how objects can be destroyed. In this
function, one identifies the table and the buttons by their ID attributes,
and thus it isn't necessary to navigate the document tree looking for
them.
// One can also destroy HTML objects using the DOM.
function destroyView() {
objecttodestroy = document.getElementById("viewtable")
body = getBody()
body.removeChild(objecttodestroy)
// Now destroy the buttons.
objecttodestroy = document.getElementById("whosaid")
body.removeChild(objecttodestroy)
objecttodestroy = document.getElementById("goaway")
body.removeChild(objecttodestroy)
}
| Listing 8.
The destroyView() function.
Conclusion
After a glance at DHTML incompatibilities in previous versions of the
Microsoft Internet Explorer and Netscape Navigator browsers, this article
explored a simple dynamic document that creates, modifies, and destroys
HTML elements using the W3C's DOM API for JavaScript. This example also
shows how to begin implementing the MVC design pattern in a user
interface. Finally, this sample illustrates the new syntax for web pages
introduced with XHTML.
Mitch Gould is a human-factors programmer for General Picture in
Forest Grove, Oregon. This article is adapted from his book-in-progress,
Human Factors Programming using the DOM, a guide to innovative user
interfaces for the Mozilla browser. Article copyright 1999 © by General
Picture. All rights reserved.
JavaScript
for the MVC example Understanding the Document Object
Model
| .gif) |
.gif) |
.gif) |
|