Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JavaScript for the MVC example

, January 01, 2002


WebReview.com: Understanding the Document Object Model

Understanding the Document Object Model

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) </td>
<td valign="top">creates a new tag. </td>
</tr>
<tr>
<td valign="top"><pre>
createDocumentFragment() </td>
<td valign="top">creates a new DocumentFragment. </td>
</tr>
<tr>
<td valign="top"><pre>
createTextNode(data) </td>
<td valign="top">creates new content for the document. </td>
</tr>
<tr><td><pre>createAttribute(name) </td>
<td valign="top"> creates an attribute for a tag.
</td>
</tr>
<tr><td valign="top"><pre>getElementsByTagName(tagname)  </td>
<td> returns a list of matching nodes (NodeList).
</td>
</tr>
<tr><td colspan="2"><b>Selected HTMLDocument Methods</b><br />
<font face="verdana, helvetica, sans-serif" size="1"><i>An HTMLDocument object has these methods in addition to the methods of Document.</i></font></td>
</tr>
<tr><td valign="top"><pre>open() </td>
<td>Opens a document for new content. 
</td>
</tr>
<tr><td valign="top"><pre>close()  </td>
<td> Closes a document.</td>
</tr>
<tr><td valign="top"><pre>write(text) </td>
<td> Appends the text to the open document. 
</td>
</tr>
<tr><td valign="top"><pre>writeln(text)</td>
<td> Appends text and a new line to the open document. 
</td>
</tr>
<tr><td valign="top"><pre>getElementById(elementId)  </td>
<td> returns the element with the given ID. 
</td>
</tr>
<tr><td colspan="2"><b>Selected Node Methods</b></td>
</tr>
<tr><td valign="top"><pre>insertBefore(newChild, refChild)</td>
<td valign="top">Inserts a child node into the document before the given node.
</td>
</tr>
<tr><td valign="top"><pre>replaceChild(newChild, oldChild)</td>
<td valign="top">Replaces the old node with the new node.
</td>
</tr>
<tr><td valign="top"><pre>removeChild(oldChild)</td>
<td valign="top">Removes the child node from the document.
</td>
</tr>
<tr><td valign="top"><pre>appendChild(newChild)</td>
<td valign="top">Adds the node to the document.</td>
</tr>
<tr><td valign="top"><pre>hasChildNodes()</td>
<td valign="top"> Returns true if the node has children.</td>
</tr>
<tr><td valign="top"><pre>cloneNode(deep)</td>
<td valign="top"> Generates a copy of the tree under the given node.</td>
</tr>
<tr><td colspan="2"><b>Selected Element Methods</b><br />
<font face="verdana, helvetica, sans-serif" size="1"><i>An Element has these methods in addition to the methods of Node.</i></font></td>
</tr>
<tr><td valign="top"><pre>getAttribute(name)</td>
<td valign="top">Gets the value of the named attribute. </td>
</tr>
<tr><td valign="top"><pre>setAttribute(name, value)</td>
<td valign="top">Sets the value of the named attribute.</td>
</tr>
<tr><td valign="top"><pre>removeAttribute(name)</td>
<td valign="top">Removes the named attribute.</td>
</tr>
<tr><td valign="top"><pre>getElementsByTagName(name)</td>
<td valign="top"> Returns a list of matching nodes (NodeList).</td>
</tr>
</table>

<font face="verdana" size="1"><b>Table 1. Sample list of critical DOM methods.</b></font>
</p>

<h3>Creating the view</h3>




Nodes are created using the <code>createElement</code> and <code>createTextNode</code> 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 <code>TBODY</code> element must be explicitly created and added to the tree. If the <code>TBODY</code> 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.
</p>




Our example's <code>createView()</code> function creates table-row and -cell elements and appends them as children to <code>TBODY</code>, as shown in Listing 6.
</p>
 
 


 <table width="100%" cellpadding="3" cellspacing="4" border="1">
<tr>
<td valign="top">
<pre>
 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


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.