An Introduction to the W3C DOM

Steven G. Chipman

Principal Software Engineer, AOL

What is it?

Dom Deluise

We'll Skip the History Lesson

But briefly...

In the old days, Netscape had a DOM and so did Microsoft.

Today's modern browsers support the W3C's DOM specification, and the world of DHTML and DOM Scripting is a much happier place.

A DOM Visualization

The Document Object Model

Nodes the Word

The DOM is made up of nodes. The important ones for us are:

// this will always alert 1 alert(document.getElementsByTagName("div")[0].nodeType);

See the Gecko DOM Reference for more on the other node types.

The DOM Interface

These should be very familiar to you if you've done any DOM Scripting in the past few years.

getElementById

Provides direct access to a node with the id passed to it:

var oElement = document.getElementById("myElement")

Which would then allow us to do this:

oElement.style.backgroundColor = "#FF0000";

getElementsByTagName

Provides access to all nodes of "tagName"

var oDivs = document.getElementsByTagName("div");

Which would allow us to do this:

var i=oDivs.length; while(i-->0) { oDivs[i].className = "myNewClass"; }

getElementById & getElementsByTagName

We can use these two methods in conjunction.

var oElement = document.getElementById("myElement"); var oDivs = oElement.getElementsByTagName("div");

This will give us only the DIV elements that descend from myElement.

The Descendants - Milo Goes to College Album Cover

Speaking of Children...

Clara Chipman

childNodes

Provides access to all the child nodes of the specified element.

var oElement = document.getElementById("myElement"); var oChildNodes = oElement.childNodes;

childNodes vs getElementsByTagName("*")

getElementsByTagName ignores the parent/child relationship and will return every element node that descends from the top level element.

childNodes will return only those nodes (of all types) that are direct descendants of the top level element.

A Quick Example...

<div> <p>Hello <em>audience!</em></p> </div>

getElementsByTagName("*") executed on the div element will return p,em

childNodes of the div element will return p and #text for the tab.

parentNode

Provides access to the parent node of the specified element.

<div><p id="myParagraph">hello, audience!</p></div>

The following would return a reference to the DIV...

var oParent = document.getElementById("myParagraph").parentNode;

A parentNode will always be either an element node, a document node or a documentFragment node. Otherwise, it will be null.

var oElement = document.getElementById("myElement"); var oString = ""; while(oElement.parentNode) { oString += oElement.parentNode.nodeName + " "; oElement = oElement.parentNode; }

The above code walks from myElement all the way up to the root element of the document and gives us the hierarchy.

childNodes and parentNode

So, we could do something like this:

var oChildNodes = document.getElementById("myElement").childNodes; var i = oChildNodes.length; while(i-->0) { if(oChildNodes[i].nodeType == 3) oChildNodes[i].parentNode.removeChild(oChildNodes[i]); }

The above would remove any child nodes of myElement that are a node type of 3 - a text node.

Does removeChild work in Movie Theaters?*

* Kimberly Blessing's Joke

removeChild

Removes the specified child node from the DOM.

var oElement = document.getElementById("myElement"); oElement.parentNode.removeChild(oElement);

The Remove Children Favelet - An example of removeChild in action.

appendChild

Appends the specified element to the specified node.

var oElement = document.createElement("div"); document.getElementById("myElement").appendChild(oElement);

Resulting in this markup:

<div id="myElement"> <p>Hello, audience!</p> <div></div> </div>

replaceChild

Replaces the specified element with a new element.

var oldParagraph = document.getElementsByTagName("p")[0]; var newParagraph = document.createElement("p"); oldParagraph.parentNode.replaceChild(oldParagraph,newParagraph);

firstChild & lastChild

Provides access to the first and last child node of the specified element.

<p id="myParagraph">Hello <em>audience!</em></p>

This will return a reference to the text node containing "Hello":

var oFirstChild = document.getElementById("myParagraph").firstChild;

And this will return the emphasis element:

var oLastChild = document.getElementById("myParagraph").lastChild;

firstChild is useful, really!

Consider the following:

var oElement = document.getElementById("myElement"); while(oElement.firstChild) oElement.removeChild(oElement.firstChild);

In a world where innerHTML is read-only, thats an easy way to blow out the contents of an element.

Node Creation

createElement & createTextNode

Creates an element or a text node in memory.

var oElement = document.createElement("p"); var oText = document.createTextNode("Hello, audience!"); oElement.appendChild(oText); document.getElementsByTagName("body")[0].appendChild(oElement);

Would create markup like this...

<body> ... <p>Hello, Audience!</p> </body>

A createTextNode Gotcha

When using createTextNode, you can't use HTML entities like &amp;. You must use the unicode value: \u0026.

See my unicode lookup tool for assistance.

createDocumentFragment

Creates a lightweight document object.

var oFragment = document.createDocumentFragment();

A key benefit of the documentFragment is that it suffers little overheard when used as a parentNode in a loop construct.

A createDocumentFragment Example

var oFragment = document.createDocumentFragment(); var i = 1000; while(i-->0) { var oElement = document.createElement("div"); oFragment.appendChild(oElement); } document.getElementsByTagName("body")[0].appendChild(oFragment);

The above is twice as fast (1 second vs 2.1 seconds) as appending the created DIVs directly to the body element as they are created.

cloneNode

Clones the specified node. If "true" specified as its argument, a "deep clone" is performed and all of its contents are cloned as well, otherwise a "shallow clone" is performed and only the element itself is cloned.

var dolly = document.getElementById("myElement").cloneNode(true);

caveat - while node attributes are cloned, programatically applied event handlers are not!

Dolly the Sheep

Using cloneNode to Clear innerHTML

This is much faster than the previously mentioned while loop over firstChild.

var oElement = document.getElementById("myElement"); oElement.parentNode.replaceChild(oElement.cloneNode(false),oElement);

insertBefore

Inserts an element into the DOM before the specified element.

var oElement = document.getElementById("myElement"); var oSpan = document.createElement("span"); oElement.parentNode.insertBefore(oSpan,oElement);

insertAfter

It doesn't exist!

var oElement = document.getElementById("myElement"); var oSpan = document.createElement("span"); oElement.parentNode.insertBefore(oSpan,oElement.nextSibling);

But that will do the same.

nextSibling & previousSibling

Provides access to the next or previous nodes in relation to the specified node.

<div id="myDiv"> <p>Hello, Audience!</p> <span>Thanks for coming today.</span> </div> // reference to the span var oNode1 = document.getElementById("myDiv").firstChild.nextSibling; // reference to the p var oNode2 = document.getElementById("myDiv").lastChild.previousSibling;

Let's Put it All (or most of it) to Use

The "Show Titles" Script

We'll write a quick script to access all of the anchor elements in the document, check to see if they have a title attribute and if so, append it to the DOM.

Show Titles: Step One

The first step is to get a handle on all of the anchor elements in the document. We'll do that like so:

var oAnchors = document.getElementsByTagName("a");

Show Titles: Step Two

Now that we have the anchor elements, we'll set up our loop:

var oAnchors = document.getElementsByTagName("a"); var i = oAnchors.length; while(i-->0) { ... }

Show Titles: Step Three

Check to see if the anchor has a title attribute:

var oAnchors = document.getElementsByTagName("a"); var i = oAnchors.length; while(i-->0) { var oTitle = oAnchors[i].getAttribute("title"); if(oTitle) { ... } }

Show Titles: Step Four

Create a text node with the value of the anchor's title attribute:

var oAnchors = document.getElementsByTagName("a"); var i = oAnchors.length; while(i-->0) { var oTitle = oAnchors[i].getAttribute("title"); if(oTitle) { var oText = document.createTextNode(" | " + oTitle); ... } }

Show Titles: Step Five

Finally, we'll insert the new node into the DOM after the anchor element:

var oAnchors = document.getElementsByTagName("a"); var i = oAnchors.length; while(i-->0) { var oTitle = oAnchors[i].getAttribute("title"); if(oTitle) { var oText = document.createTextNode(" | " + oTitle); oAnchors[i].parentNode.insertBefore(oText,oAnchors[i].nextSibling); } }

Show Titles in Action

Show Titles as a Favelet:

Tools

Resources & Further Reading

Questions?

Thanks for coming!