Chapter 3

The Document Object Model

WHAT YOU WILL LEARN IN THIS CHAPTER:

  • Looking at your document as a tree
  • Accessing the DOM from your code
  • Editing your document from the DOM

A web page is an HTML document containing markup tags and content. But, as you develop web apps, you’ll find it more helpful to think of the document as a tree-like structure filled with branches, limbs, and leaf nodes. This “document as hierarchy” concept is exactly what is meant when you hear the term Document Object Model (DOM).

In this chapter, I introduce you to the DOM and walk you through the basics you need to access and manipulate the DOM from your application code.

WHAT IS THE DOM?

The Document Object Model provides a scripting interface into an HTML document so that you can work with all of its elements within a hierarchical structure. As a result, you can navigate through the document structure with JavaScript to access and manipulate anything inside it.

The DOM is a standard that was originated by the W3C, the Web standards body. There are three DOM levels:

  • Level 1: Level 1 contains the core functionality to be able to script the DOM for an HTML or XML document. Any modern browser provides support for the Level 1 version of the DOM.
  • Level 2: Level 2 support extends DOM support to include new core functionality, event listeners, and CSS DOM. Safari and other browsers provide generally strong support for most aspects of DOM2.
  • Level 3: Level 3 adds support for newer properties and methods, keyboard events, and XPath. Safari provides strong support for Level 3.

DOM AS A TREE

As I mentioned at the start of the chapter, you’ll find it helpful to think of the DOM as a hierarchical tree that contains all of the parts of the HTML document — elements, attributes, text, comments, and so on. In DOM lingo, these individual pieces are known as nodes.

When working with HTML markup, the primary building block of your document is the element. However, when working with the DOM, the basic piece you will work with is the node. Remember that all nodes are not identical. While the DOM tree is dominated by element nodes, there are several other types that you need to be aware of.

Just like a family tree in real life, each of the nodes are interconnected. A node can have a parent, a sibling, child, and the occasional half-crazy “grandparent” that no one talks about. The document object is the family patriarch and serves as the container for all of the nodes (the descendants) of the HTML file. The document object has no corresponding HTML element. The html, head, and body elements are all contained by document.

One point I should stress is that a DOM hierarchy is not identical to the natural element hierarchy you find in an HTML document. Take, for example, the following plain vanilla file:

<html>
<head>
<title>Really Awesome Web Page</title>
</head>
<body>
<div id="container">
<h1>Pretty awesome heading</h1>
<p id="para1">Pretty lame paragraph</p>
</div>
</body>
</html>

The levels of indentation show the natural element hierarchy in the document. Or, I can express the same hierarchy in a visual tree, as shown in Figure 3-1.

Every HTML element in the DOM has properties that correspond to the element’s attributes. For example, an img has src and alt properties. However, DOM elements also have a set of common properties and methods.

In the DOM, elements are just one type of node. As a result, you also have to express other nodes of the document, such as attributes and text content, in the tree as well. The text content inside an element is a child node of the parent element. So, for example, Pretty lame paragraph is considered a child node of the p element that contains it.

Attributes are also considered nodes, but are a bit of a special case. They don’t participate in the parent/child relationships that the other nodes do. Instead, they are accessed as properties of the element node that contains it. Figure 3-2 shows the same document in a DOM tree.

When working with the DOM in JavaScript, you typically work with the document object and DOM element objects. Each DOM element object has childNodes and attributes properties for accessing these nodes, as shown in Figure 3-3. As Figure 3-3 illustrates, each node has a nodeType property that you can use to determine the type of node you are working with.

Before you begin to access the DOM, it is important to take a moment to identify the node types that the DOM contains that you’ll work with. The nodeType property of each DOM node returns a constant from the Node object that represents the type of node. These include:

  • Node.ELEMENT_NODE
  • Node.ATTRIBUTE_NODE
  • Node.TEXT_NODE
  • Node.CDATA_SECTION_NODE
  • Node.ENTITY_REFERENCE_NODE
  • Node.ENTITY_NODE
  • Node.PROCESSING_INSTRUCTION_NODE
  • Node.COMMENT_NODE
  • Node.DOCUMENT_NODE
  • Node.DOCUMENT_TYPE_NODE
  • Node.DOCUMENT_FRAGMENT_NODE
  • Node.NOTATION_NODE

Each of the Node constants represent an unsigned short number. ELEMENT_NODE represents 1, ATTRIBUTE_NODE equals 2, and so on.

ACCESSING THE DOM FROM JAVASCRIPT

You access the document object through a named object called, appropriately enough, document. Therefore, if you want to access a document object’s property or method, you can do so directly. For example:

document.open();

For other elements and nodes in the DOM, there are a variety of ways to access them, as described in the following sections.

Accessing a Specific Element

When you know the id of an element, the most common way in which you access the element node is by using getElementById(). This method searches through the DOM looking for the one node that has an id value that matches the string you specify. For example, consider the following:

<html>
<body>
<div id="header">
<p>Rawhide!</p>
</div>
</body>
</html>

If you want to access the div, you could use the following line:

var d = document.getElementById("header");

The variable d now references the div id="header" element. You can now use the d variable to access its properties and perform methods on it. For example, to set its text color, you could use the following:

d.style.color = #cccccc;

Accessing a Set of Elements

In addition to accessing a specific element, you’ll also find occasions in which you’ll want to access all element nodes of a given kind. You can also access a set of elements by using the document object’s getElementsByTagName() method. This method takes an HTML element as its parameter:

var e  = document.getElementsByTagName(elementName);

This method returns all of the elements with the specified tag name as a NodeList, or a collection of nodes ordered by their appearance in the document.

For example, to get all of the p elements in a document and return as a NodeList, you could type the following:

var cPara = document.getElementsByTagName("p");

After you have the collection of elements through either of these techniques, you can access a particular node in the NodeList by using its index number. For example, to access the first p element in the document, you write this:

var p1 = cPara[0];

If you are accessing a specific p element, such as the first, you could combine both lines into a single call:

var p1 =  document.getElementByTagName("p")[0];

This returns the first p element and assigns it to the p1 variable.

More commonly, you could use it in a for loop to perform an action on each element:

for (var i=0; i<cPara.length; i++)
{
    var para = cPara[i];
    para.style.color = #000000;
}

This code block iterates through each p element in the document and changes its text color to black. Notice that because the cPara variable is a nodeList, you can access its length property to determine the number of elements returned.

In addition, you can use getElementsByTagName() to return all element nodes inside of the calling element by using * as the parameter:

var cElements = document.getElementsByTagName("*");

This call returns all of the descending elements inside the document, regardless of the level of hierarchy they sit on.

Accessing Family Members

You can access nodes related to other nodes through more “familial ways” through several node properties:

  • parentNode returns the parent node of the current element.
  • firstChild gets the first child of a node.
  • lastChild gets the last child of a node.
  • previousSibling gets the node just before the current one in the parent’s NodeList.
  • nextSibling gets the node just after the current one in the parent’s NodeList.
  • childNodes gets all of the children of a node.

For example, you can use parentNode property to get the parent node of a given element. For example, consider the following HTML snippet:

<div id="container">
<p id="p1"></p>
</div>

The following JavaScript returns a reference to the div element:

iParent = document.getElementById("p1").parentNode;

A parent node must be either the document, element node, or a document fragment.

In addition, suppose you want to return all of the nodes that are direct children under an element, you could use the childNodes property instead to retrieve its children as a NodeList and store it in a variable. Here’s what you would code:

var c = document.getElementById("container");
var cChildren = c.childNodes;

In this code, the element with an id of container is assigned to the c variable. Its children are then assigned to the cChildren variable and are ready for action.

You can also use the hasChildNodes() method to check to see whether an element has child nodes. For example:

var c = document.getElementById("container");
if (c.hasChildNodes() == true)
{
    var cChildren = c.childNodes;
}

Retrieving Attributes

Attributes are technically nodes in a DOM, but don’t think of them as being equal to element nodes. You cannot, for example, retrieve a list of id attribute nodes through some sort of GetAttributeByName() method. In fact, except in rare occasions, there is no practical reason for doing so because attributes are inextricably associated with an element. Not surprisingly, attributes are accessible through a DOM element by accessing its attributes property or by using one of three methods.

Accessing All Attributes

The object that returns from this property is called NamedNodeMap object, which is similar to a NodeList except that its items are listed in arbitrary order, rather than a specific defined order.

If I want to retrieve a list of attributes for a given element, I might write something like the following:

var atts = document.getElementById("container").attributes;

You can then work with the attributes by accessing their name and value properties. For example:

var atts = document.getElementById("container").attributes;
var str = "";
 
for(var i=0; i<atts.length; i++)
{
str += "Name:" + atts[i].name + " Value:" + atts[i].value + "<br/>";
}

This code block gets the attributes for an element and then outputs the name and value for each attribute into the str variable.

You can check to see whether an element has any attributes at all by using the hasAttributes() method:

var ctr = document.getElementById("container");
if (ctr.hasAttributes() == true)
{
    var atts = ctr.attributes;
}

Accessing a Specific Attribute

You can also access a specific attribute using the element’s getAttribute() method. Simply enter the name of the attribute as the parameter. Suppose, for example, that I want to get the href attribute from a link:

var att = document.getElementById("book").getAttribute("href");

You can also check for the existence of an attribute with the hasAttribute() method. To check to see whether an element has a style attribute, use this code:

var cntMajor = document.getElementById("contentMajor");
if ( cntMajor.hasAttribute("style))
{
   var att = c.getAttribute("style");
}

MANIPULATING THE DOM

You can do more than use the DOM to look at a document. In fact, you can also manipulate it — creating nodes, adding them to the DOM tree, editing nodes, and removing parts of a document for which you have no more use.

In the sections that follow, I show you how to add and remove various parts of a document to and from the DOM.

Creating an Element and Other Nodes

The document object has several methods used to create nodes. I’m covering the creation of element nodes now; read about other nodes in the “Creating Other Types Nodes” section later in this chapter.

To create an element, use the document.createElement() method. This method creates an instance of the element specified by the parameter. For example, to create a p element, use this:

var para = document.createElement("p");

The createElement() method creates the p element and returns the node, which is assigned to the para variable.

Note that the act of creating an element does not automatically add it to the DOM tree. The instance only exists in your script until you explicitly add it. It’s in a sort of limbo land. A node without a country, if you will. Instead, you need to insert it into the document hierarchy to be a card-carrying node of the DOM.

Adding a Node to the DOM

DOM elements have two methods that you can use to add a node into the tree:

  • appendChild(node) adds the node as a child at the end of the current element’s childNodes list.
  • insertBefore(newNode, targetNode) adds a new element as a child of the current node, but inserts it just before the node specified by the targetNode parameter.

TRY IT OUT: Adding a DOM Node

To show you how to add a node, follow these steps.

1. Create the following HTML document in your text editor and then save the document as BIDHJ-Ch03-Ex1.html.

image
<html>
<head>
<title>FavFilms</title>
</head>
<body>
<div id="content">
<h1 id="header1">Welcome to the World of Cinema</p>
<p id="p1">Favorite films are shown below:</p>
<ol id="movieList">
<li id="film_30329">The Shawshank Redemption</li>
<li id="film_30202">Casablanca</li>
<li id="film_20216">Braveheart</li>
<li id="film_28337">Groundhog Day</li>
<ol>
</div>
</body>
</html>

Code snippet BIDHJ-Ch03-Ex1.html

2. Add the following script code in the document head and save your file:

image
<script type="application/x-javascript">
var fa = document.createElement("li");
document.getElementById("movieList").appendChild(fa);
var fb = document.createElement("li");
var casa = document.getElementById("film_30202");
document.getElementById("movieList.).insertBefore(fb, casa);
 
var fb = document.createElement("li");
var br = document.getElementById("film_20216");
document.getElementById("movieList.).insertBefore(fb, br.nextSibling);
</script>

Code snippet BIDHJ-Ch03-Ex1.html

How It Works

When this document loads, the script is executed. The first two lines of code append an empty li element to the end of the list. The next three add a blank li item in the spot just before Casablanca by using insertBefore().

Although there is no insertAfter() method, you can perform the equivalent functionality by using insertBefore() in combination with the nextSibling property of the targetNode. That’s exactly what I did in the final part of the script to add a node after Braveheart.

It’s time to look at our results. The ordered list is now updated to include the new elements I added:

<ol id="movieList">
<li id="film_30329">The Shawshank Redemption</li>
<li/>
<li id="film_30202">Casablanca</li>
<li id="film_20216">Braveheart</li>
<li/>
<li id="film_28337">Groundhog Day</li>
<li/>
<ol>

Creating Other Elements

The document object contains methods for creating the rest of the common node types: attribute, text, comment, and document fragment. Each of these methods return a node that you can add into your document using appendChild() or insertBefore().

An attribute node represent an element’s attribute. To create an attribute, you use createAttribute(). For example:

var a = document.createAttribute("alt");
a.nodeValue = "Welcome to DOM and DOMMER."
document.getElementById("logo").appendChild(a);

A text node is as a container of text. When you are first introduced to the DOM, it is probably natural to think of text being a property of an element. But, text inside of an element is contained by a child text node. To create a text node, use createTextNode(). For example, the following code snippet creates a text node and adds it onto the end of a p element with an id of para:

var t = document.createTextNode("I scream for ice cream.");
document.getElementById("para").appendChild(t);

A comment node represents an HTML comment. To create a comment node, use createComment():

var c = document.createComment("Insert new toolbar here.");
document.getElementById("toolbar_container").appendChild(c);

Finally, a document fragment is a temporary “off-line” document that you can use to create and modify part of a tree before you add the fragment to the actual DOM tree. When manipulating the DOM, working with a document fragment during the processing of a subtree is much more efficient (less overhead) than always working directly with the actual DOM itself. To create a document fragment, use createDocumentFragment(). For example, the following script creates a document fragment, adds nodes to it, and then finally adds it to the document:

var df = document.createDocumentFragment();
 
for(var i=0; i<30; i++)
{
var e = document.createElement("p");
var t = document.createTextNode("Line " + i);
e.appendChild(t);
df.appendChild(e);
}
 
document.getElementById("content").appendChild(df);

TRY IT OUT: Adding a Complete Node

Now that you know how to create the other node types, I want to revisit the movie list example. Instead of adding empty li elements, add the attribute and text nodes needed to fill out the ordered list appropriately. Use the following.

1. Create the following HTML document in your text editor and then save the document as BIDHJ-Ch03-Ex2.html.

image
<html>
<head>
<title>FavFilms</title>
</head>
<body>
<div id="content">
<h1 id="header1">Welcome to the World of Cinema</p>
<p id="p1">Favorite films are shown below:</p>
<ol id="movieList">
<li id="film_30329">The Shawshank Redemption</li>
<li id="film_30202">Casablanca</li>
<li id="film_20216">Braveheart</li>
<li id="film_28337">Groundhog Day</li>
<ol>
</div>
</body>
</html>

Code snippet BIDHJ-Ch03-Ex2.html

2. Add the following script code in the document head and save your file:

image
<script type="application/x-javascript">
    var e; // Element
    var a; // Attribute
    var t; // Text
    var nd; // Target/Parent
 
    e = document.createElement("li");
    a = document.createAttribute("id", "film_29299");
    t = document.createTextNode("Babette's Feast");
    e.appendChild(a);
    e.appendChild(t);
    document.getElementById("movieList").appendChild(e);
 
    e = document.createElement("li");
    a = document.createAttribute("id", "film_29323");
    t = document.createTextNode("Amélie (Le Fabuleux destin d'Amélie Poulain)");
    e.appendChild(a);
    e.appendChild(t);
    nd = document.getElementById("film_30202");
    document.getElementById("movieList.).insertBefore(e, nd);
    e = document.createElement("li");
    a = document.createAttribute("id", "film_30276");
    t = document.createTextNode("Vertigo");
    e.appendChild(a);
    e.appendChild(t);
    nd = document.getElementById("film_20216");
    document.getElementById("movieList.).insertBefore(f, nd.nextSibling);
</script>

Code snippet BIDHJ-Ch03-Ex2.html

How It Works

In this example, the JavaScript is executed when the document is loaded. The script uses createElement(), createAttribute(), and createTextNode() methods of the document to create a full, complete li element to add to the list. It then uses appendChild() and insertBefore() to add the elements in the desired order in the list. The ordered list now looks like the following when the script is executed:

<ol id="movieList">
<li id="film_30329">The Shawshank Redemption</li>
<li id="film_29323">Amélie (Le Fabuleux destin d'Amélie Poulain)</li>
<li id="film_30202">Casablanca</li>
<li id="film_20216">Braveheart</li>
<li id="film_30276">Vertigo</li>
<li id="film_28337">Groundhog Day</li>
<li id="film_29299">Babette's Feast</li>
<ol>

Setting a Value to an Attribute

The setAttribute() method is a shortcut to creating an attribute and adding it to an element. This method sets the attribute value for an element, but if the attribute doesn’t exist, the method goes ahead and creates it. It takes the attribute name and value as parameters:

setAttribute("name", "value")

For example, if you want to change the href of a link, you could use the following:

document.getElementById("myblog").setAttribute("href",
   "http://richwagnerwords.com");

Here’s a little more detailed example of using setAtttribute() to clean up a document. Suppose you have an HTML document that has attributes with uppercase names. If you want to convert them to all lowercase, you could the following script:

var all = document.getElementsByTagName("*");
for(var i=0; i<all.length; i++)
{
    var e = all[i];   
 
    for(var j=0; j<all.length; j++)
    {
        e.attibutes[j].setAttribute(all.attibutes[j].nodeName.toLowerCase());
    }
}

The script looks for all of the elements in a document and returns them as a NodeList. The first for loop iterates through all of the elements and assigns the e variable to be the current element in the loop. The second for loop cycles through all of the attributes of e and uses setAttribute() in combination with toLowerCase().

Moving a Node

Although there is no moveNode() method defined in JavaScript, appendChild() and insertBefore() do the same thing. In addition to adding new nodes you create, these two methods enable you to move a node from one location to another in the tree. For example, consider the following document:

<html>
<head>
</head>
<body>
<div id="header">
<img id="logoImg" src="logo.png"/>
</div>
<div id="footer">
</div>
</body>
</html>

If you want to move the image from the header to the footer, you write:

var i = document.getElementById("logoImg");
document.getElementById("footer").appendChild(i);

When this code is executed, the appendChild() method moves the logoImg from any location and adds it as a child to the footer div.

Cloning a Node

You can clone a node and add it elsewhere to the document tree by using a DOM element’s cloneNode() method. This method copies the specified node and returns a new instance that is not part of the DOM. You can then add it wherever you want using the familiar appendChild() and insertBefore() methods.

This method takes a single Boolean parameter:

cloneNode(deepBoolean)

If the parameter is set to true then all subnodes of the current node are copied along with it.

When cloning nodes, there actually is a danger element. Using cloneNode(), every part of the node comes with it, including its id. As a result, you need to be sure to update the id before you add it to the document tree.

To demonstrate, I begin with the following document:

<html>
<head>
</head>
<body>
<div id="right_box">
<p id="main">All content should begin with this sentence.</p>
</div>
<div id="left_box">
</div>
</body>
</html>

Suppose I want to copy the paragraph to the left_box div. To do so, I could write this:

var cn = document.getElementById("right_box").cloneNode(true);
cn.setAtttribute("id", "main_left");
document.getElementById("left_box").appendChild(cn);

Removing a Node from the DOM

To remove a node, you call the removeChild() method of its parent, specifying it as the parameter. For example, to remove a paragraph with an id="p1", you could use the following code snippet:

var cp = document.getElementById("p1");
cp.parentNode.removeChild(cp);

In this code, the cp variable is assigned the returning node of the specified paragraph element. Using parentNode to reference its parent, the removeChild() method is then called, specifying cp as the node to delete. The paragraph is removed from the DOM.

Or, suppose you want to remove all of the children in an element. To do so, you can loop through all of the child nodes and delete them one at a time. Here’s the code you could use:

while ( bodyContent.childNodes[0] )
{
bodyContent.removeChild( bodyContent.childNodes[0];
}

In this routine, the while loop checks to see if there is a child node for the current node. If so, then the child node is removed using removeChild(). This process repeats until there no childNodes[0].

Removing an Attribute

Suppose you have had it with an attribute and simply want to rid yourself of the pain and aggravation. Or maybe you just don’t have a use for it anymore. If so, you can use the removeAttribute() method to delete an attribute from the DOM. The following, rather draconian script removes all the alt attributes from the document — simply out of spite! Here’s the code:

var kitAndKaboodle = document.getElementsByTagName("*");
for(var i=0; i<kitAndKaboodle.length; i++)
{
    kitAndKaboodle[i].removeAttribute("alt");
}

EXERCISES

1. What is the primary “building block” of the DOM?

2. True or False? The DOM hierarchy must always be fully identical to the natural element hierarchy of an HTML document.

3. What is a NodeList?

4. What is a document fragment?

Answers to the Exercises can be found in Appendix A.

• WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
Accessing elements from the DOM Use document.getElementById() or document.getElementsByTagName().
Accessing attributes Use the attributes property of a node to get a list of attributes; use getAttribute() to get a specific attribute value.
Adding to or editing the DOM Use document.createElement() to create an element in your DOM. Add to the DOM using appendChild() or insertBefore().
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset