<%response.buffer=true%> <% yere = year(date()) Response.Expires = 0 %> DOMIntro
<DOM>
  spacerspacer

DOM Intro

DOM Defined: The DOM (Document Object Model) as it applies to XHTML documents, is an XML based API (Application Programmers Interface) for accessing, creating and manipulating XHTML objects. XML has it's own DOM, which has a parallel development path.

The Birth of DHTML: In 1997, when the browser wars between Netscape and Microsoft were in full swing, there was little cooperation between the rival camps. DHTML (Dynamic HTML) was a marketing term used by Netscape and Microsoft to describe their version 4 browser capabilities that were based largely on proprietary object models. Using these proprietary models, objects on web pages could move, shrink, grow or change color based on user interaction. The Netscape DOM used proprietary elements called "layers" to give a unique ID to an element on a web page:

var myObj = document.layers["myElement"];

The Microsoft DOM used the keyword "all" to similar effect:

var myObj = document.all["myElement"];

The differences didn't stop there however. Even the implementation of positioning an element on a web page were syntactically different for each object model. Developers had to learn 2 different object models, "sniff" browser versions or object handling capabilities and use branching code to build cross browser pages.

We still use the term DHTML to define "moving" HTML elements, such as a flyout menus. DHTML today uses CSS to define and format objects on the page, The Document Object Model (DOM) to locate and trigger the objects, and JavaScript to change, hide or move those objects.

Positioning: For an element to move on a page it must be out of the normal flow of the page, so for example a div could sit on top of the HTML document. This element would usually be absolutely positioned so it could move on top of other elements.

Once an element is out of flow (absolutely positioned) we use an x and y coordinate relative to the top left corner of the browser window as (0,0) to determine where it is in relation to the page. Later we'll find out there is a z coordinate that determines how high above the HTML document an absolutely positioned element is, so different moving elements can be on different levels.

We'll have an entire lesson on this topic, later!

DOM Levels: The DOM Level 1 was a W3C recommendation by October of 1998. It was intended to "stop the madness" of proprietary object models, and was an attempt to unify the models that all browsers should follow. DOM Level 1 defines the framework for mapping a document, and accessing objects on an XHTML web page.

The proprietary object models that existed previously (IE4 & NN4, called the "4s" collectively) were deemed DOM Level 0 in retrospect to indicate their "non-compliance" with the DOM Levels that followed.

Specifications exist for DOM Levels 2 and 3: DOM Levels Defined

Browser DOM Compliance (ascending order)
Netscape 4.x Level 0 (existed prior to DOM Level 1)
Internet Explorer 4.x Level 0 (existed prior to DOM Level 1)
Opera 1.0 - 6.0 none
Internet Explorer 5.0 Minimal Level 1 *
Internet Explorer 5.5+ Nearly all of Level 1 (80% of marketplace is here!!)
Safari 1.0+/Konquerer 2.0+ Level 1
Opera 7.0+ Almost all of Level 1, partial Level 2
Netscape 6+/Mozilla/Firefox Level 1, Level 2, partial Level 3

*(We may still care about IE5, because if we load Windows 98 on a machine, that is the loaded browser version!)

Since the majority of the browsers we work with support DOM Level 1, that will be our focus. The rest of the code on this page depends on at least the existence of a DOM1 compliant browser.

HTML becomes XHTML: By 2001 it was determined that HTML did not fit well with the DOM. It was inconsistent, allowing single quotes, double quotes, no quotes on attributes. It was not case sensitive for tags (elements).

To facilitate changing a document dynamically, a stricter standard needed to be applied to HTML. That standard is XHTML. XHTML applies XML restrictions on the HTML page. This is the reason for lower case in the 'name' portion of name/value pairs of an attribute, and the use of double qoutes for all attributes.

The following is NOT XHTML compliant:

onMouseOver='myJS();'

The following is XHTML compliant:

onmouseover="myJS();"

In the context of the DOM or XML, the 'tags' as we know them are elements, and the name/value pairs (attributes) become the most frequently used items in changing the DOM document. Here are the main changes from HTML to XHTML:

  • tags are now called elements
  • elements and attributes must all be lower case
  • elements must have a closing tag or be self closing
  • all attributes must be name/value pairs and use double quotes (no single word attributes)

Let's go over the last two. Elements in XML must all have opening and closing tags. This is normal for a table or paragraph tag, but was about the break tag or horizontal rule or image? This is why we have the last forward slash in these tags. Since there is no natural closing tag for these elements, we self close the tags:

  • <br> becomes <br />
  • <hr> becomes <hr />
  • <img src="mypic.jpg"> becomes <img src="mypic.jpg" />

Note the required space just before the forward slash. This is required to make a tag self closing and fit the requirement of all tags must have a closing tag.

Attributes must also have no single word elements. We used to be able to do the following to indicate a radio button was checked:

<input type="radio" name="color" value="red" checked>

Now we have to do this instead to be XHTML compliant:

<input type="radio" name="color" value="red" checked="checked" />

Looks funny, huh? Now you know why these attributes look redundant. They were designed to be single words, and when changed to be XHTML compliant they make uncomfortable name/value paired attributes!

Note also the old version of the radio button did not have the forward slash. It also needs to be self closed!

Object/Feature Capability Sniffing: Previously we "sniffed" to determine the version of browser with which we were working. This is effective in some cases, especially when we know a "rogue" browser has a particularly bad reaction to what we are doing with our code. We can then accomodate that browser.

What if we want to find out if we are working with a DOM Level 1 browser? It is usually more efficient to "sniff" the capability of the current browser than it is to determine the version. Here is an example you may have seen before:

if (document.images){
//preload images, etc.
}

This code checks to see if the current browser can work with the images object, which features an array of all the images in the document. If the code returns false, the browser is primitive (version 3 or less) but we don't care which version, just whether we can work with the images object (for preload) or not.

We can use the same logic to test whether a browser is generally DOM Level 1 compatible, as well. We do this by testing the "getElementById()" method:

if (document.getElementById){
//bingo! DOM1 browser!!
}

We can now wrap this "if" statement around any JavaScript that assumes a DOM Level 1 browser.

BOM vs. DOM: We can think of the DOM as a subset of the BOM, or Browser Object Model. The BOM consists of the larger group of browser objects, of which "document" is one part! Remember the "navigator", "window" and "location" objects? All of these exist outside of the document, and are better thought of as BOM, not DOM.

However, inside the document object, we can navigate the certain elements either via the old school BOM or the new school DOM. When we use the BOM to navigate the document, we must crawl the tree down to the element using the name attribute:

window.document.myForm.myTextBox.value;

In this sample we have mapped out the entire path from the window object, from a form named myForm, down to our text box named myTextBox inside it down to the value as currently stored in the text box (as text typed by the user on a live form in browser memory).

By comparison, DOM level 1 predominantly uses the id attribute to find a single element on the page. All elements involved must use the id element to be easily singled out in the new DOM:

document.getElementById("myTextBox").value;

For both of these methods (BOM and DOM) to work on the same element, we would need to match the "name" and "id" attributes in the element:

<input type="text" name="myTextBox" id="myTextBox" />

This is why wysiwyg editors (like Dreamweaver) assign id and name attributes to the same values! There is at least one critical difference between using these methods. Using The BOM version (name, not id attribute) can call up an array of objects (radio buttons & checkboxes all have the same name to perform as a group!) but by design using "getElementById()" can only retrieve one element, because a unique ID can only exist once on a page! It is for reasons like this the 'old school' BOM will be around with us for some time.

Name Allows Arrays, ID Allows Single Elements Only: Once we work with forms we'll find out that a set of radio buttons must all have the same name to work together as a group:

<input type="radio" name="color" value="red" />
<input type="radio" name="color" value="blue" />
<input type="radio" name="color" value="yellow" />

The id attribute has nothing to do with submitting a form, only with accessing an element in browser memory! When the form is submitted to the server for processing, it's all about the name & value , baby! The id attribute does not submit. The data submitted via a form to the server for processing is the name/value combination from each checked, selected or typed in form element.

If in the above radio button example a user checked the radio button with a name of color and a value of red, the following data is transmitted to the server when the form is submitted:

color=red

Therefore the old school BOM may never go away (at least this part!)

Accessing Multiple Elements via DOM level 1: We have seen above how to access any element on a web page using DOM level 1 "getElementById()" method. There are other powerful methods we can use to access elements on a web page. We can use "getElementsByTagName()" for example, to return an array of all the "<font>" tags, across the entire page:

var aFonts = document.getElementsByTagName("font");
for(var x=0; x<aFonts.length; x++)
{
    alert("Turn Font " + (x + 1) + " RED!");
    aFonts[x].setAttribute("color","#ff0000");
}

In this example we have used another DOM method, "setAttribute" to change the color attribute of each font on the page, in turn! Here is an example which also checks the current font color with the "getAttribute" method, and changes the color between red & blue: Set Fonts

Wildcards: We can also use "wild cards" to access an array of ALL the elements at once:

aElements = document.getElementsByTagName("*"); //note the parens to distinguish the asterisk!!

We can use this with the "getElementById" to get all the tags INSIDE an element:

myDiv = document.getElementById("someDiv");
aElements = myDiv.getElementsByTagName("*"); //now we have all the tags in the div "someDiv"

Nodes: Sometimes we want to access more than tags or elements identified by their ID numbers. Elements, attributes and even text are all examples of what are called "nodes". Nodes can not only be identified by type, but can also be identified by their relationship to other nodes. Look at this example:

theBody = document.getElementsByTagName("body")[0]; //there can only be one body, hence the zero!
aChildren = theBody.childNodes; //aChildren array includes ALL nodes inside the body tag!!

In the above example, all nodes (elements, attributes and text nodes) are in an array called "aChildren". The relationship "childNodes" indicates the nodes exist INSIDE the body tags!

The family tree symbolism is easy for humans to remember, which is why it is used to determine the "relative" position of one node to another. If a node is INCLUDED INSIDE another node, it is a "childNode". If a node INCLUDES another node (it is OUTSIDE) the node, it is called a "parentNode". Both of these identify the relationship between the nodes.

Node Type: We can determine what "type" of node we are currently working with, however, the "nodeType" property returns an integer, not a string! If the current node is an Element, the nodeType returned is 1, an Attribute is a 2, and text is a 3. In all there are 12 node types:

theBody = document.getElementsByTagName("body")[0]; //there can only be one body, hence the zero!
aChildren = theBody.childNodes; //aChildren array includes ALL nodes inside the body tag!!
myNodes = "";
for(x=0;x<aChildren.length;x++){
    myNodes += "Node " + (x + 1) + ": " + aChildren[x].nodeType + "\n" ;
}
alert(myNodes);

simplenode1.htm getNodes1.htm getNodes2.htm

Node Values: If we want to access text inside an element, we can do it using the "nodeValue" of a "childNode" of an element. Here is an example of extracting the text inside a paragraph tag with an ID of "myP":

myVal = document.getElementById("myP").childNodes[0].nodeValue;

We can use an equivalent statement to get that first node, using "firstChild":

myVal = document.getElementById("myP").firstChild.nodeValue;

"nodeValue" is a "read and write" property, meaning we can also put our own text into the element:

document.getElementById("myP").childNodes[0].firstChild = myVal; //write a value INTO the element!

We can also access the LAST element in the children array, with "lastChild", if applicable. With this example,it would pull up the same value, since there is only one text element in a "p" tag!

More About Nodes: There are several methods and properties we can access programmatically using nodes. Here are some of the most useful:

Property/Method Returns Description
nodeName string Name of the node
nodeType integer 1-12 defines type of the node
nodeValue string Contents of node (depends on type)
ownerDocument document Applicable document for this node
firstChild node Pointer to first node in childNodes
lastChild node Pointer to the last node in childNodes
childNodes NodeList(array) All child nodes
previousSibling node/null Pointer to previous sibling node, null if N/A
nextSibling node/null Pointer to next sibling, if last, N/A (null)
hasChildNodes() Boolean True if contains at least one node
appendChild(node) node Adds node to end of childNodes
removeChild(node) node Removes node from childNodes
replaceChild(new,old) node Replace old node in childNodes
appendChild(node) node Adds node to end of childNodes
insertBefore(new,ref) node Inserts new BEFORE ref node
attributes NamedNodeMap All attributes of an element

Here are the element nodeType values: JavaScript Kit nodeTypes

Here is a great DOM reference from the folks at Gecko: DOM Reference

A Single Page Image Gallery: Here is an example from the book, "DOM Scripting" that uses some of the examples on this page to create a single page image gallery. Here is function that performs all the magic:

function showPic(myPic) {
    var source = myPic.getAttribute("href");
    var placeholder = document.getElementById("placeholder");
    placeholder.setAttribute("src",source);
    var text = myPic.getAttribute("title");
    var description = document.getElementById("description");
    description.firstChild.nodeValue = text;
}

The function is called via an onClick event in an "a" tag:

<a href="images/rose.jpg" title="A red, red rose" onclick="showPic(this); return false;">Rose</a>

When the link is clicked, the image source (in the href attribute) is retrieved via the getAttribute() method. An image source with an ID of "placeholder" is replaced with this image with the setAttribute() method.

Then, the value of the "title" attribute of the "a" tag is extracted into a variable called "text".

Lastly, the "firstChild" of the description element (where the caption is for the picture) is loaded into the description! Gallery1

Chaining: In JavaScript we can "chain" together methods which will fire one after the other. View a different version of the showPic() function above, in which DOM methods are chained together:

function showPic(myPic){
document.getElementById("placeholder").setAttribute("src",myPic.getAttribute("href"));
document.getElementById("description").firstChild.nodeValue = myPic.getAttribute("title");
}

Study the first version of the showPic() method and contrast it to the second version. Here's the example: gallery2.htm

More!: For more info on DOM Scripting, view the following tutorial: Brainjar's DOM Intro

   
Print this Page Back To Top

2000- <%=yere%> newMANIC INC, All rights reserved