You are on page 1of 53

Dom

Scripting
Content
The Document Object Model, called DOM for short, is the way modern
browsers store and represent web pages.   page 2
Dom scripting is like forestry.  page 3
Navigating the DOM tree : finding the shortest path  page 4
Before we get into the juicy details of DOM scripting, we need to talk about
safety.   page 5
Three things you ought to know about the Document object  page 10
Seven things you ought to know about Nodes  page 12
What is a Node List  page 17
Four things you ought to know about Text Nodes  page 18
What to do with Attr Nodes  page 21
Two things you ought to know about Element Nodes  page 23
Something wicked about forms. It is not a bug.  page 28
The 80/20 rule  page 31
Identification through IDs, not names.  page 32
Styling using Dom scripting : with or without class  page 33
The innerHTML property : the sledgehammer way   page 36
Server-side DOM  page 39
Dom scripting best practices  page 41
eet
IE7 bug report : the onclick event-handler   page 44
Lean and sw
More : recommended reading  page 52
Credits  page 53
T he Document Object Model, called DOM for short, is the way modern
browsers store and represent web pages.
Browsers organize the markup of a web page into a tree structure.
That tree stands on its crown : when one moves up the tree, one moves toward the root (that is
the <html> element) ; whereas when one moves down the tree, one moves toward the leaves.
The Document Object Model is called the Document Object Model because it represents a
Document (that is your web page) using Objects (called nodes, which have their methods and
properties), and it provides for that document a tree Model.

Document Object Model


The metaphore used for the Document Object Model is that of Attribute nodes aren’t children
a family tree where children live in monoparental homes : a node of the element nodes they
may have only one parent. Attribute nodes are the only exception modify. We’ll see how to access
and edit these nodes later.
to this rule : they have no kin relationship with any node in the
tree, hence no parent and no sibling. Attribute nodes are like hats
and shoes : they are worn by HTML elements, but they don’t make
the man. Child nodes of the same parent are called siblings.
Current browsers, such as Internet Explorer 6 and 7, Firefox 1.x, and Opera 7 and 8, for
Windows and Mac, support the core functionality of the W3C DOM in a consistent way—minus
a few Internet Explorer glitches which we will cover later.

Document Object Model


Node.property; The DOM presents a document as a hierarchy of interconnected Node objects.
Node.method(); Some types of nodes may have child nodes, and others are leaf nodes that have
nothing below them in the document structure. We’re dealing with objects,
and JavaScript is our scripting language, so the dot syntax is used to access the
properties and methods of the Node objects in the document tree.

The DOM is a language-independant Application Programming Interface


The Document Object Model is not part of JavaScript but a separate entity
existing outside of it. Although we can use JavaScript to manipulate DOM
objects, other scripting languages can access them as well, such as Perl, PHP,
Python and Ruby.

z2Z Bchill
Document Object Model
Since frames have been out of fashion, browsers display one document per window at a time.
(When frames are used, each frame has its own Document object.) We just type in “document”
in our JavaScript code to use the document object, it’s that simple.

D om scripting is like forestry.

Everytime we use the Document object and call methods on it, we’re DOM scripting. The
Document object gives us Javascript access to any part of the tree (we’ll see how in a bit). Using
this object, we can add to, delete, modify, swap and move any piece of markup on the page. We
can also change the attributes of an element on the fly.
Let’s take a look at the anatomy of an HTML element :
<a href=”http://www.w3.org”>The World Wide Web Consortium</a>

This is the closing tag.


This is the opening tag with a This is the CONTENT of the <a> element.
href attribute and its value.

The matching tags and the content—ALL of this stuff—is called an element.
The content of any html element is where you find the children of that element.* In
our example, the text “The World Wide Web Consortium” is the content of the <a> element.
Hence, it is a child node (and in this case, a Text Node) of the <a> Element node. The
<a> element contains no nested elements. It is the presence of text and elements nested
inside other elements that makes up the tree structure of the document object.
* The content of any html element is where you find the children of that element, and a child cannot
be found any place else. Since an attribute, such as href, ain’t part of the content of the element it
modifies, it can’t be a child of that element—nor of any other element in the document tree.
Any DOM scripting must be done after the DOM tree is built.
We use the event handler onload on the window object to keep track
of when the tree is built. The property “onload” is the
window.onload = myFunction; name of the function the
browser “calls back” when the
function myFunction() { ..... } DOM tree is built.
There are things we may want to do to a web page once it’s loaded,
but there are also things we may want to do when the browser catches
other “events” such as a mouse click or when a specific form field loses
focus or when AJAX data is received—long after the DOM tree is built.

z3Z Bchill
N avigating the DOM tree : finding the shortest path
Node creation comes in 2 easy steps : actual creation of a node,
then insertion of that node at the right location in the tree.
We have to get before we set, and we have to create before we insert. In order to get
an element that already exists on the page, or in order to find where to insert a new one, we need
to navigate the Document tree. We can use any combination of these 4 methods to find our
way around :
First method : We can use sign-posted The method getElementById(“idName”)
 elements. returns a handle to the single element on
the page that has an id attribute equal to
“idName”.
<>
Second method : We can target a type
of element, say all <p> elements. The method getElementsByTagName(“p”) can be used
Third method : We can target all either on the document object or on an Element ; it
elements that belong to a class—or, for returns a list (or collection or array) of handles to
 that matter, we may target all elements
that have a particular value for a particular
ALL paragraphs that are “below” the node.
For that, we go through a list of elements, read
attribute, besides class. the value of their class attribute and compare it
Fourth method : We can move up and to a set value, to determine if there’s a match.

star t here down and sideways using Node properties For example, to access the second child
such as parent, firstChild, and of an element node, we’d use the following

nextSibling. Javascript code :
get there
var myNode = elNode.firstChild.nextSibling;

We can add an id attribute to whichever element we want to later grab when DOM scripting,
that is, whichever element we’ll want to edit, move or remove. If there is a bunch of elements
we want to do the same stuff to, we set their class attribute to the same value. An element can
belong to more than one class, as shown in this example :
<p class=”first intro news”> ...... content of paragraph...... </p>

This paragraph is in 3 different classes all at once : first, intro and news. The names of the classes must be
separated by a space. It makes no difference in which order these names are typed ; however, it does matter the
order in which the rules for these different classes are defined in the stylesheet. If two or more classes provide a
different value for the same property, the style which is applied is the last one that is defined in the stylesheet.

The DOM allows us to make changes to a web page without


reloading it. We can modify the DOM tree as we please and the changes we
make are immediately reflected on the page. We use DOM scripting to order
changes, and the browser updates the page for us. When our code runs, as it
runs, only the parts of the page that need to be updated are updated.

zZ Bchill
B efore we get into the juicy details of DOM scripting, we need to talk
about safety.

Protect your eyes


and your head. SAFETY
COMES
FIRST
Reduce the time you spend
staring unblinkingly at your
computer screen, wondering
what the heck is going wrong.
Here is what we have covered so far : the content of any html element is where we find the
children of that element. It is the presence of text and elements nested inside other elements that
makes up the tree structure of the document object. Any DOM scripting must be done after the
DOM tree is built. We have to get before we set, and we have to create before we insert. The
DOM allows us to make changes to a web page without reloading it. The browser updates the
page, as our code runs.
But what if we don’t see the changes we expect, how will we know where we err ? We ought to
be able to debug our code as well as examine the DOM tree at any time.

Preparation in Internet Explorer  and 7


In Windows XP, a security feature of Internet Explorer 6 and 7 blocks Javascript from running on
local pages. It can be extremely annoying to constantly OK the “active content” of the page we’re
loading in the browser. There is an easy and safe workaround :

1. From the menu, click on Tools, then select “Internet Options...” from the submenu.

2. In the Internet Options dialog, click on the Advanced tab.

3. Under Security options, enable the checkbox “Allow active content to run in files from
My Computer.”

We have to close Internet Explorer and launch it again in order for the change to
take effect. 

That’s called the


“info bar”. Clicking
C:\Documents and Settings\chill\Bureau\test.html
on it to OK the
active content is a
pain in the arse.

z5Z Bchill
First safety rule : debug your Javascript
One way to debug Javascript code, which works the same in all browsers, is to rely on a
Javascript library to log messages on our web page. With such library, we don’t need to keep on
closing alert() windows, neither do we have to make do with the browser debug console. We
can use David F. Miller’s fvlogger library, featured in A List Apart , and downloadable here. We log
an “info”, “debug”, “warning”, or “error” message from our Javascript code, whenever we want,
by simply inserting a function call in our script : info(“...”); debug(“...”); warn(“...”); or error(“...”).
These functions are defined in fvlogger.js. Each function takes a string as argument. The message
we log can be anything that helps us understand our script’s operation. Errors logged by the
browser are captured by fvlogger and displayed on the web page, under the category
“fatal”. 

How to use fvlogger in four easy steps :


First, download the fvlogger zip file and extract its content in a subfolder in your web site
directory—say a subfolder called fvlogger.

Second, link to the fvlogger stylesheet in the head of your html file, using the link element :

<link rel="stylesheet" type="text/css" href="fvlogger/logger.css" />

Third, link to the library file, again, in the head section, and don’t forget to leave a space between
the opening and closing tags of your script element so that all browsers can read the line
correctly :

<script type="text/javascript" src="fvlogger/logger.js"> </script>

Fourth, copy and paste the fvlogger console somewhere between the opening and closing tags
of the body element on your web page. You can find that console in the index.html file you have
extracted from fvlogger.zip ; the console is the div block with id fvlogger. Copy that whole block
on your page.

Make sure that the above <script> element is typed in before any other, as you’ll call functions
defined in fvlogger.js inside all your other Javascript files. Also, you may want to add the div
console at the very end of the body section of your html file, to avoid breaking your layout.
When you’re done, you can use info(), debug(), warn() and error() from anywhere in your scripts,
to your heart’s content.

<div id="fvlogger">

<dl>

... That will be our Javascript


console, on our web page.
</dl>

</div>

zZ Bchill
Second safety rule : keep an eye on that DOM tree
As we attempt to modify the DOM tree with Javascript, we may want to examine the effect our
code has on the DOM tree as our code runs. We can call a function that writes the structure
of the entire tree on our web page, but we might as well use a plug-in to inspect that DOM
tree. Firefox has an extension that does just that, and it is called, rightly so, the DOM Inspector.
Contrary to what is often said, one needs not uninstall and resinstall Firefox with a box checked
to get the Dom Inspector. You simply go into Tools in your Firefox menu and add the extension
from there, while you are connected to the Internet, and voilà.

The equivalent widget in Internet Explorer is not free

The Commercial License for the IE WebDeveloper


(formerly known as the IE DOM Inspector) is 79 US$
while the Non-Commercial License is 59 US$. You may
download the “add-on” evaluation version and try it for
15 days, then kiss it good-bye. You may download the
older version (good old IE DOM Inspector) and try it
for 15 days then kiss it good-bye as well. A much better
and extremely suitable alternative is to install the free
beta Internet Explorer Developer Toolbar. Works
in IE 6 and IE 7. For ever. Then click on “View DOM”.

Kiss my backside.

Example of a simple DOM tree, as seen in Firefox


Once we have installed the Dom Inspector extension in Firefox, we can access it through the Firefox
menu under Tools or we can use the following shortcut : CTRL-SHIFT-I (I as in Inspector).

.... <body><p id="main">When looking for advice, ask <a href="http://www.


tradetricks.org/">a professional</a>.</p></body> .... The node type of an
Yep I am a francophone. Element node is alw y
ays 1.
In that corner
is the list of
attribute nodes
associated with the
<a> element, along
with their values.

z7Z Bchill
Let’s play The What is game.
Print this page and grab a pen. A bunch of definitions
are given below. Your mission is to figure out what
concept they define. Set your egg timer to 7 minutes.
Ready, set, go. (Answers are on the next page.)

What is a n
Any single piece of markup or chunk of text on the page is it. Three commonly used types are : text,
element and attribute. An empty element is one that never has a closing tag.
Examples are images (<img />), line breaks (<br />), and links
What is a l (<link />). Empty elements never have content.
Using the metaphore of the botanical tree, this term describes an empty element (such as
<img />), or a nonempty element without content (such as an empty <div> or an empty <span>),
or a chunk of text, or a comment. An example of a nonempty element without content would be :
<p id=“intro” class=“bubblegum”></p>
What is an a See ? There’s nothing there.
Although it is part of the DOM tree, it is not attached to it, i.e. it has no parent.

What is a c
Anything that is attached to the DOM tree is one, except the root.

What are s
Nodes that have the same parent.

What is the r
The topmost node in the DOM tree — the <html> element on a web page.

What is a p
It is an element that has content. Hint : these elements always
require a closing tag.
What is a n element
A type of element that may or may not have content.

What is an a
It is not a child of the element it modifies.

zZ Bchill
Answers to The What is game.
What is a node
Any single piece of markup or chunk of text on the page is it. Four
commonly used types of nodes are the Text node, the Element node, the
Attr node and the Comment node.

What is a leaf (node)


This term describes an empty element, such as <img />, or a nonempty element without content,
such as an empty <div> or an empty <span>, or a chunk of text. It is a childless node — it has no
node below it in the DOM tree. An attribute node cannot be a leaf because it is not attached to
the DOM tree.

What is an attribute (node)


Although it is part of the DOM tree, it is not attached to it, i.e. it has no parent.

What is a child (node)


Anything that is attached to the tree.

What are siblings


Nodes that have the same parent.

What is the root (element)


The topmost node in the DOM tree — which is the <html> element on a web page.

What is a parent (node)


It is an element that has content.

What is a nonempty element


An element that may or may not have content, which content can be text and/or other nested
elements.

What is an attribute (node)


It is not a child of the element it modifies.

zZ Bchill
T hree things you ought to know about the Document object

Remember the 2 easy steps of node creation ? The first one is


the actual creation of the node, and the second is the insertion or
attachment of that node at the precisely right location in the tree.

1 First thing : we always create Text and Element Nodes using the Document object.
(When it comes to Attr Nodes, it’s a different story, check out the next page.) Remember our “we
have to create before we insert” motto ? There are two steps.

Creation of an Element Node


<p>
var elNode = document.createElement(string tagName);

document.createElement() is a method of the Document object. This method takes one string
argument, which is the tag name of the element to be created. That argument is not optional, and
it is not case sensitive. The method returns a handle to the newly created Element Node. That
element is not yet attached to the DOM tree : the attachment has to be done in a second step,
not to be forgotten, using the handle to the new Node as well as the insertBefore(), appendChild()
or replaceChild() method on a target Node — which we must get a handle of. We may want to
add content to our new Element Node, unless of course we are creating an empty element, such
as an image (<img />). We may put content within the element by either appending child Nodes to
it, or we can use the innerHTML property although it has not been sanctioned by W3C. See the
innerHTML section for details on the later. To create a paragraph, we’d use the following code :

var newParagraph = document.createElement("p"); This assigns your newly created


element to the variable
Creation of a Text Node
abc
var txtNode = document.createTextNode(string someText);
newParagraph.

document.createTextNode() is a method of the Document object. It takes one string argument,


which is not optional. The method returns a handle to the newly created Text Node. That node
is not yet attached to the DOM tree : the attachment has to be done in a second step, using
the handle to the new Text Node as well as the insertBefore(), appendChild() or replaceChild()
method on a target Node — whom we must get a handle of as well.

var newTextNode = document.createTextNode("I watched the 24 hours series on


 DVD within 24 hours & I loved every minute of it.");

If we want to use a special character, say the copyright symbol (©), we resort to a Unicode escape
sequence (\uXXXX), rather than an HTML entity. In Javascript, we can surround our string data
with either single or double quotes : we use single quotes if we have double quotes inside our
string argument, or we use double quotes if we have single quotes inside our string argument. We
can append text to a Text Node by using the nodeValue property and the “+=” operator :

newTextNode.nodeValue += " I’d love to be interrogated by Kieffer Sutherland.";

z 10 Z Bchill
The case of the Attribute Node
Although they belong to the DOM tree, attribute nodes are not attached to it : they are not
child nodes of any node in the tree. The Element Node they modify is not their parent. An
Attr Node (that’s how it’s called) can be created and put to good use in one step, by using the
method setAttribute(string nodeName, string NodeValue) on the Element Node it modifies, where
nodeName is the name of the attribute and nodeValue is its corresponding value. If an attribute
already exists with that name, it is overwritten with NodeValue.
<a href="#start" title="Go back up"><img src="images/arrowUp.gif" /></a>

attributes attributes
Imagine that these <a> and <img> elements have been created with DOM scripting. They don’t have any
attribute so they’re pretty useless. How do we add attributes ? By getting a handle to the elements, that’d
be a good start. Then by using the setAttribute() method :
aElem.setAttribute("href", "#start");
aElem.setAttribute("title", "Go back up");
imgElem.setAttribute("src", "images/arrowUp.gif");

2 Second thing you ought to know : to find any node in the DOM tree, you have to start
looking somewhere, and that somewhere is always the Document object. We may want
to find an element that has a particular id value. Or we may be looking for all <p> elements.
var elNode = document.getElementById(string idValue); is method returns
an array of handles to
Th

var elNodes = document.getElementsByTagName(string tagName); Element Nodes. The st


1
element is elNodes[0].
Note that idValue is case sensitive while tagName isn’t. If an element on the page has an id
attribute with value “Intro” and you set idValue to “intro”, getElementById() will return null. Such
is life. Something else to keep in mind : you may also run the method getElementsByTagName()
on an Element Node ; for example, you may want to get a list of handles to all paragraphs nested
inside a particular div on the page — within that tree branch, all the way down to the leaves :
var paragraphNodes = divElement.getElementsByTagName("p");
If there is no paragraph, an empty array is returned. Both the Document object and the Element
Node support the method getElementsByTageName(). But they are not sharing getElementById().
Why do you think that is so ? On a web page — hence in a document — no two elements can
share the same value for their id attribute. Since there can only be one element that has an id
attribute set to some value, it doesn’t matter where you look on the page. That is also why
getElementById() returns one value instead of an array.

3 One last thing : you may get a handle to the <html> element of a page through a
special read-only property of the document : documentElement.
var htmlElement = document.documentElement; The <ht
ml> element Node always has two
child nodes : the <head> and the <body>.
var bodyElement = document.documentElement.lastChild;

z 11 Z Bchill
S even things you ought to know about Nodes

1 First thing : the three types of Nodes we play the most often with, when DOM
scripting, are Element Nodes, Text Nodes and Attr Nodes. All three have the following
properties : nodeType, nodeName and nodeValue.

nodeType nodeName nodeValue


(integer) (string)

Element Node 1 tag name null

Attribute Node 2 attribute name attribute value

Text Node 3 "#text" any text


READ-ONLY Get & Set value

You can set the nodeValue of Attr and Text Nodes. By “get & set”, we mean that the property
can be used either on the left or on the right hand side of the assignment operator (“=”) : we can
either “get” (read) the value, or “set” (write) it.

myTextNode.nodeValue = “Spin that wheel”;


Setting
myTextNode.nodeValue += “ and spin it again.”;

var myString = myTextNode.nodeValue; Getting

The case of case sensitivity when working with nodeName and nodeValue
Make case-insensitive comparisons : make a habit of converting to either upper or lower case
the value you read from properties such as nodeName and nodeValue before you compare it to a
literal value inside an if statement. You never know what you’re gonna get. 
if (elNode.nodeName.toLowerCase() == “img”) { alert(“this is an image”); }
if (elNode.nodeName.toUpperCase() == “IMG”) { alert(“this is an image”); }

z 12 Z Bchill
2 Second thing you ought to know about Nodes : You can navigate a DOM tree by going
up, down, or sideways from a “you are currently here” Node, given you possess a
handle to that starting point. We can only traverse nodes that are attached to the tree. Two
common types of Nodes that are attached to the tree are Element Nodes and Text Nodes. The
most notorious type of Node that is not attached to the tree is the Attr Node. When you move up
from a Node, you’re moving to the parent Node. When you move down, you’re moving to the child
Nodes. When you’re moving sideways, you’re moving to a sibling Node. You can never move
down from a Text Node. Text Nodes have no content, hence no children.

This property or method offers you ... Type

myNode.parentNode A handle to “Tha” Element Node that contains Node


moving up myNode, a.k.a the container Node.

myNode.previousSibling A handle to the sibling Node that comes before Node


moving to the left myNode. Hence on the same level with the same
parent. Returns null if myNode is the first child of
myNode.parentNode

myNode.nextSibling A handle to the sibling Node that comes after myNode. Node
moving to the right Hence on the same level with the same parent.
Returns null if there’s no more sibling.

myNode. true if myNode has children, false if it’s a leaf Node Boolean
hasChildNodes()

myNode.childNodes A list or array of handles to Nodes. There can be zero Array of


or 1 or many Nodes in that array. To find out how Nodes
many there are, use myNode.childNodes.length

myNode.firstChild A handle to the first child Node of myNode. Is null if Node


first kid (on the left) myNode has no children. g

myNode.lastChild A handle to the last child Node of myNode. Is null if Node


last kid (on the right) myNode has no children. g

ALL PROPERTIES ARE READ-ONLY (cannot be set)

It’s good practice to check if myNode has children before using these properties. Let’s say you wanted
to get rid of all child Nodes of myElNode, here’s what you’d do :

while (myElNode.hasChildNodes()) { myElNode.removeChild(myElNode.firstChild); }

z 13 Z Bchill
3 Third : to insert a Node after all children of a given node, we use
the method appendChild().
var appendedNode = givenNode.appendChild(newChild);
If newChild was already attached somewhere in the tree (rather than brand-spanking new and/
or hanging in deep space), it ain’t attached there anymore, it has been removed from its original
location, it along with all of its descendants. No cloning took place, and nodes can have only
one parent. The appended Node becomes the lastChild of givenNode. The method appendChild()
returns a handle to the added Node. This method can be called only on Element Nodes as Text
Nodes never have children.
If you wanted newChild to become the first child of givenNode, you’d do this instead :
givenNode.insertBefore(newChild, givenNode.firstChild); Which brings us to…

4 Fourth : to insert a Node before a given child Node, we use the


method insertBefore().
var insertedNode = parentNode.insertBefore(newChild, referenceChild);
With this method, newChild becomes a new child of givenNode, at the same time as it becomes
the previousSibling of referenceChild. referenceChild is the node before which newChild is to
be inserted. (Again, descendants follow along…) In the event that referenceChild is null,
newChild is appended as the last child of parentNode.  (Keep that in mind.)

5 Fifth : to remove a Node and put in its place some other Node,
we use the method replaceChild().
This method offers you a two for the price of one deal : you don’t need to — although you could
— remove a Node and then append or insert another Node where that old deleted Node was.
You can do both things with one call. newChild moves with its content (descendant nodes).
var unattachedRemovedNode = givenNode.replaceChild(newChild, oldChild);
After this operation, both oldChild and unattachedRemovedNode point to the same Node, that is
the Node that was removed. We may later insert this unattached node in the DOM tree.
unattachedRemovedNode hangs in space with its content (descendant nodes), ready to be attached somewhere else.

6 Sixth : to remove a Node, we use the method removeChild().


var unattachedRemovedNode = givenNode.removeChild(childToGetRidOf);
The method removeChild() returns a handle to the Node it has removed. So you could insert
unattachedRemovedNode somewhere else. If you intended to insert it back somewhere else
right away, you might prefer using appendChild(), insertBefore() or replaceChild() on the parent
Node that is ready to adopt it. Remember that at the very moment you attach a Node in a new
location, its older parent is grieving its loss : moving is removing. After executing removeChild(),
childToGetRidOf is still valid and points to the same Node as unattachedRemovedNode. Mmm.

z 14 Z Bchill
7 If we want to copy a Node that is attached somewhere in the DOM tree and paste it on some
other Node, along with all its descendants, what do we do ? Other puzzle : what if we had created
an Element Node and wanted to produce an exact replicate of it with all its attributes avoiding the
extraneous effort of recreating the whole thing from scratch ? The easy solution to both puzzles is
to be able to clone any Node for our own enjoyment. The last thing you ought to know about
Nodes is how to clone them :
var newChildlessNode = nodeToBeDuplicated.cloneNode(false);
var newNodeWithAllItsDescendants = nodeToBeDuplicated.cloneNode(true);
Keep this in mind : nodeToBeDuplicated needs not be attached anywhere. The method
cloneNode() makes a copy of nodeToBeDuplicated if its argument is false, or it makes a copy of
nodeToBeDuplicated and all its descendants (children, grandchildren, grand-grandchildren, etc.) if its
argument is true. All Attr Nodes are automatically copied, in both cases, thank God. The method
returns a handle to a duplicate of the node or branch.
The new node or branch must be inserted in the document tree, using either of these three methods :
appendChild(), insertBefore() or replaceChild(), on the parent Node that is ready to adopt it.
someNeedyParent.appendChild(newNodeWithAllItsDescendants);
If replaceChild() is the equivalent in DOM scripting to a CTRL-C CTRL-V, cloneNode()
followed by appendChild() is like CTRL-X, CTRL-V. 

Fill in the blanks.


Your mission is to access the first paragraph that comes after all <h3>
headings in your web page and insert at the very beginning of these paragraphs
an inline image which relative path to the HTML page is images/key.gif. Answers
are on the next page.
var myImage = . ("img");
myImage.setAttribute("src", " ");
myImage.setAttribute("alt", "Key");
var allHeadings = . ("h3");
for (var i=0; i < allHeadings.length; i++) {
var firstParagraph = allHeadings[ ]. ;
if (firstParagraph. .toLowerCase() == " ") {
var newImage = myImage. ( );
firstParagraph.insertBefore( , firstParagraph. );
}
}

z 15 Z Bchill
Solution
Our mission was to access the first paragraph that comes after all <h3>
headings in our web page and insert at the very beginning of these paragraphs
an inline image. The snappiest way of going about it is to create this image once
and clone and attach it wherever we need to.

We create the image


element only once. Then
var myImage = document.createElement("img"); we’ll clone it as we need
myImage.setAttribute("src", "images/key.gif"); it ! The alt attribute
is there for accessibility
myImage.setAttribute("alt", "Key"); reasons, if images are
disabled in the browser.
var allHeadings = document.getElementsByTagName("h3");

for (var i=0; i < allHeadings.length; i++) {


We are going through each <h3> element on the page to look
at the element that comes right after it, if there’s any.
var firstParagraph = allHeadings[i].nextSibling;
If there is no sibling to the right of the <h3> heading, firstParagraph is null. If it’s null, the
following if statement “evaluates” to false, and we goto the next iteration of the “for” loop.

if (firstParagraph.nodeName.toLowerCase() == "p") {
var newImage = myImage.cloneNode(false);
firstParagraph.insertBefore(newImage, firstParagraph.firstChild);
} We’re inserting our cloned image at the very
beginning of the paragraph’s content. An image
is an inline element, by default, so the text of
} the paragraph will wrap around it. REMEMBER
We’re checking here if indeed nextSibling is a that cloneNode() copies the Attr Nodes of the
paragraph. The nodeName of an Element Node is image automatically !
its tag name ! If we had used toUpperCase(), we
would have compared the nodeName to capital P.

z 1 Z Bchill
Tiny summary of what you should know about Nodes
One : all types of Nodes have these properties : nodeType, nodeName and nodeValue.
Two : we can traverse the DOM tree by jumping from node to node using these properties :
parentNode, firstChild, lastChild, previousSibling, nextSibling. Attribute Nodes cannot be traversed.

Three : we append a Node (and its descendants) this way :

var appendedNode = givenParentNode.appendChild(newChild);


Four : we insert a Node (and its descendants) immediately before a specified child Node this way :

var insertedNode = givenParentNode.insertBefore(newChild, referenceChild);


Five : we remove and return a specified Node, replacing it with another one, with this method (and
descendant nodes follow along, as usual) :
var removedUnattachedNode = givenParentNode.replaceChild(newChild, oldChild);
Six : we remove and return a specified child Node (and its descendants) this way :
var removedUnattachedNode = givenParentNode.removeChild(childToGetRidOf);
Seven : we clone a Node or a branch, returning its hanging duplicate, with this method :
var newUnattachedChildlessNode = nodeToBeDuplicated.cloneNode(false);
var newUnatachedNodeWithAllItsDescendants = nodeToBeDuplicated.cloneNode(true);
Eight : moving is removing and when we move a node, we always move it with all of its decendants. 

W hat is a Node List

A Node List is an ordered collection or array of Node object handles. It is the type of data
we get when using the property childNodes. It is also the type of data getElementsByTagName(“p”)
returns. For an existing Node, the property childNodes is never null, even for a leaf Node. Just
the same, the method getElementsByTagName(“p”) never returns a null value when invoked on an
existing Node. For Nodes with no children, childNodes is an array with length zero. In this example,
the variables someNode1 and someNode2 are set to the same value :
var someNode1 = ElementNode.childNodes[0]; We gotta be careful when using childNodes
var someNode2 = ElementNode.firstChild; in iterative processes (LOOPS!), it’s dynamic,
hence updated to reflect... what we’re doing. §
These too :
var someOtherNode1 = ElementNode.childNodes[ElementNode.childNodes.length-1];
var someOtherNode2 = ElementNode.lastChild;

z 17 Z Bchill
F our things you ought to know about Text Nodes

1 First thing : two sibling Text Nodes that are side by side do not collapse into a single Node.
(We will see on the next page how to make them collapse, using the normalize() method.) You can
append text to a Text Node by adding a string to its nodeValue, or, alternatively, you can attach a whole
new sibling Text Node next to it. Although that will look the same in the browser, in the second case, you
end up with two sibling Text Nodes.

Replacing all text witin a Text Node :


myTextNode.nodeValue = "This is a substitution for whatever was the old text.";

Adding text at the end (appending) :


myTextNode.nodeValue += " This text is added at the end of the old text.";

Adding text at the beginning is also doable :


myTextNode.nodeValue = "This text is added at the beginning. " + myTextNode.nodeValue;

Adding a Text Node, newTextNode, after an existing Text Node, of which handle is myTextNode :
var newTextNode = document.createTextNode(" This is added at the end in a new node.");
myTextNode.parentNode.insertBefore(newTextNode, myTextNode.nextSibling);

There may very well be no sibling to the right of myTextNode; if


there isn’t, myTextNode.nextSibling is a “null” value. If we pass a “null”
value as 2nd argument to the method insertBefore(), the new Node is
inserted as the last child of the parent node. Which happens to fulfill
our needs exactly ! All is swell !

Adding a Text Node, newTextNode, before an existing Text Node (myTextNode) :


var newTextNode = document.createTextNode(" This is added before as a new node.");
myTextNode.parentNode.insertBefore(newTextNode, myTextNode);

So how do we clean such mess ? <p>

I want to tell you something but I am a little shy about it.

Adjacent Text Nodes may or may NOT bother us, actually…


but a way to make them collapse is described on the next page.

z 18 Z Bchill
2 Secondly : A space, new line or tab caught between two elements in an html file may
become a single space Text Node in the DOM tree.
Some browsers, namely Firefox, create single whitespace Text Nodes out of adjacent tab, newline
and/or whitespace characters, used by us in the html file to format the markup. These adjacent tab,
newline and whitespace characters have no effect on how the web page is displayed in the browser.
However, we end up with a DOM tree filled with Text Nodes with nodeValue " ", snugged in
between certain elements. We shall keep this in mind when traversing the DOM tree in any browser.

Here’s a snippet of an HTML file :



<p>In <a href="#p2">part 2</a> <a href="#p3"> and 3</a> there is
such b.s.</p> 

<p>I love the article anyway.</p>

<p>Lovely!</p> 
 

 <p>Oh yes.</p> 

In the Dom Inspector in Firefox, we find here five Text Nodes that don’t have any
impact (AT ALL) on the web page display, all containing a single whitespace. These
are caused by tabs/spaces/new lines shown in locations 2, 3, 4 and 5. The whitespace
caught between the <a> elements doesn’t have an impact on how the page is rendered
for one special reason : if you take a closer look, you’ll see that in that first
paragraph there are two adjacent whitespaces to display between the “2” and the
“and” word (these are grayed out). Since these are enclosed in a <p> element, rather 
than a <pre> element, they collapse into a single whitespace. Typically, a web browser
treats multiple adjacent whitespace characters as a single space. Check this out. ∆ 
:
a ca rria ge return  
This is 
This is a t 
ab :

3

Third thing : we can get rid of empty Text Nodes and merge
adjacent ones with the method normalize(). This method must be used on the container
Node (the parent) of that branch we intend to clean up. So yes, we can make Text Nodes that sit
side by side collapse into one.
merges adjacent Text Nodes and removes
document.documentElement.normalize(); empty ones in the head and body.
This method will not get rid of Text Nodes that contain a single whitespace. The method removes
Text Nodes with nodeValue "". Text Nodes with nodeValue " " are not removed. Hence, the Text
Nodes created by Firefox out of the matter in which we have formatted our markup in the HTML
file will remain after a normalize operation. Keep this in mind.

z 19 Z Bchill
4 Fourth thing : what we put in Text Nodes is plain text, not HTML. So we should never use
html entities in Text Nodes — these characters that begin with & and end with a semicolon. Neither
should we use markup. If we want to display a special character, such as the registered trademark
symbol ®, we must use a Unicode escape sequence (\uXXXX).
Given the following Javascript code :
myTextNode.nodeValue = "Me & her are buddies, her age is much > than mine.";
myTextNode.nodeValue += " Let’s go to a caf&eacute;. Let’s go to a café.";
myTextNode.nodeValue += "\nThis a backslash: \\.";
myTextNode.nodeValue += " Lots of space for \t500$ a month.<br />";
myTextNode.nodeValue += " I saw the movie \u03c0 and liked it.";
NOT
The Text Node contained in a paragraph will be displayed like this in the browser : true for
MSIE 7

The HTML entity &eacute; is not converted, and <br /> does not create a new line, as expected.
Characters that may create problems in an HTML file because they are confused with markup, such
as > and &, are displayed correctly.
The newline escape sequence \n is converted but reduced to a single whitespace, and any adjacent
tabs, new lines and whitespace become a single whitespace. This behavior has nothing to do with Text
Nodes. It has to do with the container Node of the Text Node. If we were to insert that same Text
Node in a <pre> Element Node, rather than a <p> Node, here is how the browser would display it :

Notice the horizontal scroll bar. In a <pre> element (pre for preformatted), formatting is respected.

z 20 Z Bchill
W hat to do with Attr Nodes

Please do not skip this section. You may have heard that in practice we rarely if ever get a handle
to an Attribute Node. You may have heard that it’s best to use setAttribute(), getAttribute() and
removeAttribute() on Element Nodes, to add, change, read and get rid of attributes. (We’ll talk
about these methods at length when we cover Element Nodes.) Well... that isn’t fair, and this
section will prove it. How do Attr Nodes work when we get a handle to them — they are really
called Attr Nodes rather than Attribute Nodes — is what we will cover right here and now.
Attr Nodes are associated with Element Nodes. In the html file, the attributes are found in the
opening tag of the element, when we specify those ourselves at design time. However, some
attributes, even if they’re not defined outright at design time, find themselves in the DOM tree
set to some default value. And then of course we are free to mess around with these while DOM
scripting. Although Attr Nodes are associated with Element Nodes, their parentNode is always
null. Strictly speaking, an Att Node exists for each attribute, and it is set to a value which we
can find in a descendant Text Node. You needed not know that, but now you do. However, the
interface to an Attr Node hides this fact from us. (Now you know why you did not need to know
what you now know.) The way to change the value of an Attr Node is quite straightforward. First
we have to get a handle to the said Attr Node, for example :
var classAttr = someParagraphElement.getAttributeNode("class");
If classAttr is null, it means that no such attribute exists for our <p> element. Now if it ain’t
null, let’s try to read the value of this attribute, shall we. Remember nodeValue ?
if (classAttr) { var classValue = classAttr.nodeValue; }
Let’s say we wanted to start from fresh and change the class value to “flashy”, given that “flashy”
is defined in our stylesheet. We can get or set the property nodeValue :
if (classAttr) { classAttr.nodeValue = "flashy"; }
By setting the nodeValue property, we consent to the creation of a Text Node with set value
“flashy” and that Node thereafter becomes the only child of our Attribute Node. What if we
wanted to keep the existing value and simply add an another to it ? It is possible for an Element to
if (classAttr) { belong to more than one class !
var classValue = classAttr.nodeValue; // returns the value as a String
classValue += " retro"; // we are adding a space plus another value
classAttr.nodeValue = classValue; // we are overwriting the nodeValue
}
Or more succinctly, we can use the “+=” operator :
if (classAttr) { classAttr.value += " retro"; }
This works perfectly in all browsers that support the W3C DOM, even IE. If our paragraph has
no class Attr to begin with though, none of this works. So flip to the next page where we will
learn how to add Attr Nodes, and where there are truly no limits to what a woman can do.

z 21 Z Bchill
Hold on ! We forgot to say something

The case of the Attribute Node part deux


There is a second way to work with Attr Nodes and it is to use the property value of the Attr Node,
instead of the Node property nodeValue. This second way of going about things works just as well.
In Internet Explorer even. Really we have no complaints. The properties nodeValue and value are
perfectly interchangeable when working with Attr nodes and they both are W3C-approved. Here is
an example where we overwrite the class attribute of a paragraph using the property value :
var classAttr = someElement.getAttributeNode("class");
if (classAttr) {
classAttr.value = "flashy retro";
}
We can also use the “+=” operator on value just like we could on plain vanilla nodeValue :
if (classAttr) { classAttr.value += " retro"; }
This works perfectly in all browsers that support the W3C DOM, even Internet Explorer. Again, if our
paragraph has no class Attr to begin with, classAttr will be null, the if condition will evaluate to false,
and that will be a wrap. Let’s try to figure out an “else” procedure…

So now, how do we add an Attr node that does not exist ?


We have already seen how to create attribute nodes with the method SetAttribute(). SetAttribute()
overwrites the value of an existing Attr node or creates it and sets its value. The problem is it’s 
buggy. We’ll see how in the next section, and the way we work around that. The thing is : who likes
to work around, or even work ? We don’t. Well, it seems that many people prefer to “work around”
rather than use one way all the time, that takes a little more time, i.e. more lines of code. Right now
we will expose how to create Attr nodes the long way, the way that works all the time, in all modern
browsers, including Internet Explorer 6 and 7, and it is W3C-approved. Using our example again :

var classAttr = someElement.getAttributeNode("class"); createAttribute() works just


if (classAttr) { like createTextNode() and
classAttr.value += " reliable"; createElement(). setAttribute() is a
} else { method of the Element Node object.
var newAttr = document.createAttribute("class"); Important : in Internet Explorer 6
newAttr.value = "reliable"; and 7, so that you know, “class” is
someElement.setAttributeNode(newAttr);
a default attribute that is empty.
So the if statement will evaluate to
}
true for Internet Explorer.
}

z 22 Z Bchill
T wo things you ought to know about Element Nodes

1 First thing : we can get a list of handles to all descendant Element Nodes of a
particular type using Element.getElementsByTagName(string tagName).
Descendant Nodes are the children, grandchildren, and grand-
grandchildren of a Node. A type of Element Node is <p> or
G e
em
tEl ents
<blockquote> or <img /> or <a> or <div> or <span>. An Element

By
t
node’s type is given by its tag name — p, blockquote, img, a, div and span

agNa me()
— and we can read that tag name through the read-only Node property Element Node
nodeName. The nodeValue of an Element Node is always null. Even when
we set this Node property to some other value, it remains null. The
method getElementsByTagName() gives us handles to Element Nodes.
If we wanted to get a handle to all paragraph nodes below a given Node
— nested in that Node — then we’d use this :
var paragraphs = givenNode.getElementsByTagName("p");
If we wanted to get a handle to all Element nodes nested in a given Node, then we’d use the
universal selector, represented by an asterisk (*). It is like a wild wildcard. With it in place of a tag
name, the method getElementsByTagName() returns an array containing all the Element nodes
below the Element Node. We can also use the universal selector on the whole document :

var subElements = someElement.getElementsByTagName("*");


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

2 Second thing : we can use the methods setAttribute(), getAttribute() and


removeAttribute() on Element Nodes, to add, change, read and
get rid of their attributes.
That is, we can go the long way, the way that works all the time, in ge
tr
tAt ibut

e(
all modern browsers, including Internet Explorer 6 and 7, and which
is approved by W3C, or we can use the snappy setAttribute() and )
Element Node
getAttribute() methods that don’t always work as expected. Trying out
this code in Internet Explorer 6 and 7, and Firefox :
tA (
se

tt ri b ute
)

alert(someParagraph.getAttribute("class"));
someParagraph.setAttribute("class","retro");
The method getAttribute() gives us a null value in Internet Explorer, whether there is a class
attribute or not (and we have seen that by default there is an empty class attribute set for
certain elements in that browser). In Firefox, we get the correct value : only null if there’s no
class attribute for the element. Also, if a class attribute already exists, setAttribute() creates a
second class attribute in Internet Explorer, ignoring what’s already there. In Firefox, setAttribute()
overwrites the existing attribute. What gives, man ?

z 23 Z Bchill
The word class, as attribute name, can be used without a glitch in the same browser,
that is, Internet Explorer, as argument of methods Element.getAttributeNode() and
document.createAttribute(). However, with methods getAttribute() and setAttribute(), we cannot
get or set the following attributes : class and for. What for ? The reason offered by Microsoft is
that these words — class and for — are reserved keywords of Javascript.
The workaround usually set forth is to use two properties that are not part of the W3C DOM :
className and htmlFor. See the box below for details. We can still go the long way, though. We
can go the way that works all the time, in all modern browsers, the way that is W3C-approved,
so we shall expose it one more time. Suppose we want to overwrite the class attribute of a given
node. The way to go about it is to try to get a handle to the class Attr Node, if one exists. If one
does, we set its nodeValue. End of story. If such Attr node does not exist, we create one , set
its nodeValue  and add it to the element . Here’s that code again :
var classAttr = someElement.getAttributeNode("class");
if (classAttr) classAttr.nodeValue = "retro";
else {
var newAttr = document.createAttribute("class"); 
newAttr.nodeValue = "retro"; 
someElement.setAttributeNode(newAttr); 
}
If we want to add a class name to the class attribute, the only line we change is the second one :
if (classAttr) classAttr.nodeValue += " retro";

Class and for in Internet Explorer


We can use className instead of class, and
htmlFor instead of for to access said class and
for attributes the following way :
anyElementNode.className
someLabelNode.htmlFor
These are read and write properties of
the HTML DOM Level 0 Interface. We are
talking old dumb DOM here. If there are no
class attribute, or no for attribute, and we set
these values, a new attribute is created. We
may also use the “+=” operator here with
the className property. If there are no class
attribute, or no for attribute, and we get
these values, that is, we read them, we get an empty string value, i.e. "". Keep this in mind. 

z 2 Z Bchill
To summarize, if we wish to add a whole new class or for attribute to a node, and
we want our code to work in all browsers, including Internet Explorer, we can still
use the W3C DOM API.

Let’s go over the details :


The following, which is sanctioned by W3C, will work in all browsers except Internet Explorer :
someElement.setAttribute("class", "retro");
someLabelElement.setAttribute("for", "input1");
The following will work in Internet Explorer but it won’t work in any other browser :
someElement.setAttribute("className", "retro");
someLabelElement.setAttribute("htmlFor", "input1");
The following will work in all modern browsers, but is not sanctioned by the W3C :
someElement.className = "retro";
someLabelElement.htmlFor = "input1";
If the attribute already exists, and we know it exists, we use a clean method sanctioned by the W3C :
someElement.getAttributeNode("class").nodeValue = "retro";
Or  :
someElement.getAttributeNode("class").value = "retro";
If we are not certain that an attribute exists, we go the long way :
We get a handle to the attribute with getAttributeNode() node and check the return value. If it’s
null, then there are no such attribute, so we create one.
var classAttr = someElement.getAttributeNode("class");
if (classAttr) classAttr.nodeValue = "retro";
else {
var newAttr = document.createAttribute("class");
newAttr.nodeValue = "retro";
someElement.setAttributeNode(newAttr);
}

That being said, the methods getAttribute() and setAttribute() work top notch in
all browsers except when it comes to the class and for attributes.

z 25 Z Bchill
The method removeAttribute() resets an attribute to its default value. If there is no default
value for this attribute, the attribute is actually removed. If the attribute does not exist,
then nothing happens. The method has no return value.
Since we cannot use the words for and class as argument of the method
oveAttri
removeAttribute() in Internet Explorer, we may wonder how we will em

bu
remove a class attribute or a for attribute. We can certainly do this :

te()
someElement.className = "";
Element Node
someLabelElement.htmlFor = "";
But the Attr nodes will still exist, albeit with an empty value. Not good.
The following method is preferred, as it is sanctioned by the W3C and
works the same way as the method removeAttribute() : it either resets
the attribute to the default value or gets rid of it. The only difference is
that the argument is a handle to the attribute node, and not the attribute name :
someElement.removeAttributeNode(someElement.getAttributeNode("class"));
someLabelEl.removeAttributeNode(someElement.getAttributeNode("for"));

Important reminder :
If there are no class Attr Node for an element, and we set
the DOM Level zero propery Element.className, a new
attribute is created. If there are no class Attr Node, and
we get (read) the same property (className), we get an
empty string : "".
The W3C-approved Element.setAttributeNode(attrNode)
takes an Attr Node as argument, works all the time, and
if for that element an Attr Node already exists that has
the same NodeName, it is replaced with aforementioned
“attrNode”.

z 26 Z Bchill
Summary of what we can be done with Element Nodes
One : Element.getElementsByTagName(String tagName) gives us access to all descendant
Element nodes of the type tagName. It returns an array of nodes with length of 0, 1 or higher.
Two : We can read the value of an attribute with Element.getAttribute(String attributeName).
The value is returned. If the attribute doesn’t exist, the method returns null. The method does
not work for class nor for attributes in IE, see point no 5. Convert to either upper or lower
case before comparison in an if statement. Here, we’re checking if we have an external link :
if (link.getAttribute("href").substring(0,4).toLowerCase() == "http") {...}
Three : We can set the value of an attribute by using the following Element node method :
Element.setAttribute(String attributeName, String attributeValue). If the attribute does not exist,
it is created and set to attributeValue. If the attribute exists already, it is overwritten by the new
value. The method does not work for class and for attributes in Internet Explorer, see points
no 6 and 7. Here is an example :
hatImage.setAttribute("src", "images/topHat.gif");
Four : We can reset an attribute to its default value, or get rid of it altogether, by using the
Element node method Element.removeAttribute(String attributeName). If the attribute does not
exist, no harm is done. The method does not work for class and for attributes in Internet
Explorer, and for these see point no 8.
Five : If we want to read the value of an existing class or for attribute, we rely on the
properties of Attr Nodes. We either use nodeValue or value to read the value.
var myValue = someElement.getAttributeNode("class").nodeValue;
var myValue = someElement.getAttributeNode("class").value;
Six : If we want to modify an existing class or for attribute, we use nodeValue or value to
set the new value, as shown below :
someElement.getAttributeNode("class").nodeValue = "retro";
someElement.getAttributeNode("class").value = "retro";
Seven : If we wish to create a new class or for attribute, we rely either on the “Long and
W3C-approved way”, or use HTML DOM read and write properties, as shown below :
someElement.className = "retro";
someLabelElement.htmlFor = "input1";
Eight : If we wish to remove a class or for attribute, we use the following Element Node
method (that works for any Attr Node) :
someElement.removeAttributeNode(someElement.getAttributeNode("class"));
someLabelEl.removeAttributeNode(someElement.getAttributeNode("for"));

z 27 Z Bchill
S omething wicked about forms. It is not a bug.

Two things you need to know about forms. Two possibly time-consuming limitating things —
time-consuming if you don’t understand what’s going on. We’ll talk about this type of form :

A Form
<form action="process.php" method="POST">
<p id="nameInput">Type in your name :
<br />
Name:
<input type="text" name="name" value="" id="name" />
<br />
<input type="submit" id="submitButton" />
</p>
</form>

This won’t be a study of forms like New Yorker


artist Nancy Bowen’s “studies of forms”.

1 First thing : In Internet Explorer, certain attributes used in forms are read-only, such
as the type attribute of <input> elements. Unless the <input> element has be created
with Dom scripting and therein lies the solution.
You cannot change the value of the type attribute of <input> elements from “submit” to
“button”, for example. The Internet Explorer guys want to protect their users from malicious
scripters who may want to hijack submit buttons on the page. So this can’t be done in IE (but this
can be done in Firefox) :
var myInput = document.getElementById("submitButton");
myInput.setAttribute("type", "button");
There is a simple workaround. Unfortunately, it does not involve cloning the input node ; that
doesn’t work either. What we have to do is create a new input node from scratch. Then we can
set the value of its type attribute, once :
var oldInput = document.getElementById("submitButton"); Cloning the node then
var newInput = document.createElement("input"); setting its type attribute
won’t work either in IE. We
newInput.setAttribute("type", "button");
have to go the long way and
newInput.setAttribute("id", "submitButton"); set ALL attributes on a
oldInput.parentNode.replaceChild(newInput, oldInput); brand-spanking new node.

z 28 Z Bchill
2 Second thing : In Internet Explorer, you cannot set the name attribute of an <input>
element created with Dom scripting. Actually, you cannot set the name attribute of
any element generated through Dom scripting.
In IE, you cannot add or set a “name” attribute on <input> elements of a form that you create.
Unfortunately, the name attribute is a necessity when sending data to the server in name & value
pairs. Creating a form on the fly is something that we may want to do.
There’s an absolutely horrible workaround to this problem, that will work only in Internet
Explorer :
var input = document.createElement("<input name='name' type='text' id='name'/>");
document.getElementById("nameInput").appendChild(input);
How one can feed html text as an argument to the method createElement() is a mystery yet unsolved.

Do you know CSS well ?


If you don’t, flip the page or scroll down. If you do, take the challenge ! On
the left side are CSS rules. (A CSS rule tells the browser to select certain
elements on web pages that link to the stylesheet, and to apply a style to
them.) On the right side, write down how you would go about getting the
same elements in your script, using methods & properties of the DOM.

#disclaimer {....} var myEl = document.getElementById("disclaimer");

p#disclaimer {....}

#disclaimer p {....}

.date { .... }

z 2 Z Bchill
Do you know CSS well ? (Disclaim !)
If you don’t, flip the page or scroll down. If you do, take the challenge ! On the
left are CSS rules. (A CSS rule tells the browser to select certain elements on
web pages that link to the stylesheet, and to apply a style to them.) On the
right, write down how you would go about getting the same elements in your
script, using methods & properties of the DOM.

#disclaimer {....} var myEl = document.getElementById("disclaimer");

p#disclaimer {....} var myEl = document.getElementById("disclaimer");

if (myEl.nodeName.toLowerCase() == "p") {...}


There can be only one element on the page that has an id value equal to
“disclaimer”. Why would a CSS rule select a specific element with an id ?
Because there may be a page where a <div> may have the “disclaimer” id,
and another page where it’s a paragraph that has it. Here, the script may
run for more than one page if it is an external script, so we’re checking
if we did not get a handle to say a <div> rather than a <p>.
#disclaimer p {....} var myEl = document.getElementById("disclaimer");
var myPars = myEl.getElementsByTagName("p");
for (var i=0; i < myPars.length; i++) { .... }
Here we’re looking for all paragraphs that are descendants of
WHATEVER element has an id value equal to “disclaimer”.
.date { .... } var myEls = document.getElementsByTagName("*");

An element may belong for (var i=0; i < myEls.length; i++) {


to more than one var classAttr = myEls[i].getAttributeNode("class");
class. If it does, class if (classAttr) {
names are separated
by a whitespace in the classes = classAttr.nodeValue.split(" ");
attribute value. You for (var j=0; j < classes.length; j++) {
can also use regular if(classes[j].toLowerCase() == "date") { ... }
expressions here :
split(/\s+/); where } The CSS rule says : apply this style to whatever element has “date”
/...../ is an expression, } as a class attribute. If we don’t know which type of element
“\s” is whitespace and } to look for, then we need to look at all elements. The universal
“+” allows for many selector comes in very handy. We could also have as 1st line :
occurences. var myEls = document.documentElement.lastChild.getElementsByTagName("*");

z 30 Z Bchill
T he 80/20 rule

The 80/20 rule tells us that 80 percent of our usage of the whole DOM API will
involve 20 percent of its features. An API is an Application Programming Interface.
And an Application Programming Interface is like a remote control : on that remote,
80 % of our time will be spent using 4-5 buttons out of the 20 something. What we’ve
covered so far is enough. We could close this book right now. Most of what we’ve
seen thus far will remain fresh to us if we use DOM scripting on a regular basis.
We’ll cover two other things about the API and then call it a wrap : the innerHTML()
method and the style property. These two “controls” aren’t highly recommended.
They come with reservations because they haven’t been sanctioned by the W3C. They
are part of the HTML DOM Level zero API. But not only that…

Auto-referencing allowed : Grab my handle


Using all that we’ve covered thus far, let us accomplish a few easy tasks, just to keep things fresh.
Given that we are a Node, and given that our handle is mySelf, and given the existence of some other
Node, of which the handle is anotherNode, let’s write up the code that will allow us to get some work
done using as few Javascript statements as possible. Try and find your own answers before looking at
the magnets below. It’s just more fun.
First task. I want to add some other node before me.
Second task. I want to replace myself with some other node.
Third task. I want to reduce the fat in me. I want to
have as few nodes as possible inside of me.

Fourth task. I want to remove myself from the picture.

mySelf.parentNode.replaceChild(anotherNode, mySelf);

mySelf.parentNode.normalize();

mySelf.insertBefore(anotherNode, mySelf.firstChild);

mySelf.normalize();

mySelf.parentNode.removeChild(mySelf);

mySelf.parentNode.insertBefore(anotherNode, mySelf);

with fourth magnet. Fourth task goes with the fifth magnet.
Answers : First task goes with the last magnet. Second task goes with first magnet. Third task goes

z 31 Z Bchill
I
dentifi cation through IDs, not names.

We can still use the HTMLDocument API to get hold of forms, images or links on a web page,
in the manner in which it was done in the old days. These forms, images and links are grouped
into collections (that is arrays) of forms, images and links. Each form, image or link is to be gotten
hold of through its position in the collection (determined by the order in which it appear on the
web page), or through its name attribute. The HTMLDocument API is part of DOM level
zero. To be consistent and up to date, we prefer and advocate a modern approach.

Snippet of an HTML page with two forms


<form action="process.php" method="POST" name="form1" id="form1"> 31
<p><label for="name">Name:</label> 32
<input type="text" name="name" value="" id="name" /><br /> 33
<input type="submit" id="logInButton" value="enter" /> 34
</p> 35
</form> .... 36
<form action="process.php" method="POST" name="form2" id="form2"> 37
<p> If you prefer to lurk, press this button : <br /> 38
<input type="submit" id="guestButton" value="lurk" /> 39
</p> 40
The use of the “name” attribute for <form> is deprecated, but it is
</form> valid and very much needed for <input>, <select> and <textarea>. 41

Using the old HTMLDocument API we would reset the second form using any of these calls :
document.forms[1].reset();
document.forms.form2.reset(); The order in which items appear in collections is the same
as the order in which they appear on the page.
document.forms["form2"].reset();
document.form2.reset(); That’s the ‘name’, not the ‘id’.
Using the mother of God-approved DOM level I Document API we’d reset the second form using
either of these two calls :
document.getElementsByTagName("form")[1].reset();
document.getElementById("form2").reset(); That’s the ‘id’, not the ‘name’.
The DOM level I Document API is not aware of the existence of collections, and the XHTML
specification has deprecated the use of the name attribute, except for <input>, <select> and
<textarea> elements. For all purposes, the deprecated name attribute is used only for the sake of
bundling up data in name and value pairs to send to the server in the form of an HTTP request. Both
methods submit() and reset() can be used on forms accessed with W3C-approved methods.

z 32 Z Bchill
S tyling using Dom scripting : with or without class

Althought we may add style to a node on the fly using the property Element.style, the best way to
go about it — if we plan on changing our style in the future — is to add that node to a predefined
class. Predefined as in : defined in an external style sheet.
 In our script, we can set
the class attribute of a
index.html node we wish to style to
style.css some value, say “new”.
script.js
.new {...}

 And in the stylesheet,


link we can define a rule
s to
for “new” — a rule
that we may modify
any time in the future,
We should always keep separate content, style and behavior. right in the *.css file.
Dom scripting is behavior and should be put in an external
javascript file. Style rules should be defined in a *.css file.

1 How to add an Element Node to a predefined class.


First, let’s make sure that the class is predefined in the stylesheet. Then do either of the following :
someElementNode.className += " new"; // only if there’s already a class name
var classAttr = someElementNode.getAttributeNode("class");
Or if (classAttr) classAttr.nodeValue += " new";
else { var newAttr = document.createAttribute("class");
newAttr.value = "new";
someElementNode.setAttributeNode(newAttr); }

2 How to swap classes.


First we retrieve all whitespace-separated class names and build an array with them. Then we
loop through that array, looking for the class name to swap. When we find it, we replace it. When
we’re done, we convert the updated array into a String, that we pass to the property className.
var classes = someElementNode.className.split(" "); We could alternatively use the
for (var i=0; i < classes.length; i++) { getAttributeNode() method here.
if (classes[i] == classToBeSwapped) { classes[i] = newClassName; }
}
We could also use a regular expression
someElementNode.className = classes.join(" "); as argument (Same for split()).

z 33 Z Bchill
Styling without class. Style : You mind if I smoke ? Class : If you must. Style : I don’t
have to. Class : Then don’t.

If you must
Using Node.style works just like applying inline style using the style attribute.
If you want to style without class, use the style property followed by a dot followed by the name of
the CSS property in camel case. Hyphenation is prohibited : the hyphen could get confused with the
subtraction operator.
someElementNode.style.font-size = "0.92em"; Not good.
someElementNode.style.fontSize = "0.92em";
someElementNode.style.font-weight = "bold"; Not good.
someElementNode.style.fontWeight = "bold";
someElementNode.style.color = "blue"; Hyphenation Prohibited

Changing the stylesheet using DOM scripting


Let’s pretend we wanted to change the stylesheet from default.css to coffeeKrisp.css. How do we
do that ? Well, it’s easy. Let’s examine where the stylesheet url is specified in the HTML page. The
following markup is to be found between the opening and closing tags of the <head> element :

<link rel="stylesheet" type="text/css" href="default.css" />

This is standard stuff. This is an empty element.


This is where the url of the stylesheet is specified.

We can add an id attribute to the <link> element in the markup, say id="myStyleSheet", and access
the link using the method getElementById("myStyleSheet") to change the href attribute. Or,
alternatively, we can switch stylesheets using one of these three methods :
document.getElementsByTagName("link")[0].getAttributeNode("href").value = "coffeeKrisp.css";
document.getElementsByTagName("link")[0].getAttributeNode("href").nodeValue = "coffeeKrisp.css";
document.getElementsByTagName("link")[0].setAttribute("href","coffeeKrisp.css");
To disable a stylesheet, do this :
linkElement.setAttribute("disabled", "disabled");
To re-enable it, do either of these things :
linkElement.removeAttribute("disabled");
linkElement.removeAttributeNode("disabled");
Performing a removeAttribute() on the disabled attribute removes it altogether. There is no such
thing as a default value for that attribute.

z 3 Z Bchill
Making things disappear and reappear on the web page
We can make whole blocks disappear on the web page, leaving either empty space behind — or
nothing behind. For that, we define rules for two new classes in our stylesheet, classes to use in
our scripts : hidden and leavingNoSpace. For the hidden class definition, we’ll set the CSS property
visibility to hidden. For the leavingNoSpace class, we’ll set the CSS property display to none.
We simply add elements to these classes to make them disappear, and remove them from these classes
to make them reappear. With its visibility property set to hidden, an element takes up as much
space as if it was there, but it’s empty space. With its display property set no none, an element takes
up no space on the page, it’s as if the element has been removed from the DOM tree.

script.js  We add or remove the


style.css specific class for the
element.
.hidden
{...}
.leavingNoSpace
 We define two new rules, one for the class hidden
{...} and another for leavingNoSpace. And we put
these rules at the bottom of the stylesheet, so
that they have a higher specificity ranking.
.hidden { visibility:"hidden"; }
.leavingNoSpace { display:"none"; }

An important thing to remember, always, is that elements may belong to more than one class : the
HTML DOM property className can list more than one class name, separated by a whitespace.
When we add an element to say class hidden, we don’t want to overwrite the className attribute, for
when we make the element reappear, we want the original CSS styling to be applied to it. For example,
we could define the function toggleHide(Element node el, Boolean hide) to hide or show elements :

function toggleHide(el, hide) { If there are no class Attr Node,


var hidden = false; and we assign the className
var classes = el.className.split(" "); property to a variable, what we
for (var i=0; i < classes.length; i++) { are assigning is an empty string.
if (classes[i].toLowerCase() == "hidden") { Using split() on an empty string
if (hide) hidden = true; returns an array of length zero.
else classes.splice(i, 1);
break; The toggleHide() function assumes
} that there is no doublet of the
} class name “hidden” in the class
if (!hidden && hide) classes.push("hidden"); attribute value.
el.className = classes.join(" ");
}

To hide an element, we could call the function with these arguments :


toggleHide(document.getElementById("elToHideAndShow"), true);

z 35 Z Bchill
T he innerHTML property : the sledgehammer way

Although the property ElementNode.innerHTML is not part


of the W3C DOM, it is widely supported. The innerHTML We can either get
property of an element is the HTML text contained within GET & the innerHTML
that element, not including the opening and closing tags of value, or we can set
SET it (write it).
the element itself. Setting this property replaces the content
of an element : the HTML text is parsed by the browser and
the web page and document object are updated.

<div id=”response”><p>Welcome to <span class=”title”>My Life</span>.</p></div>

This is the opening tag with The closing tag.


an id attribute and its value. This is the CONTENT of the <div> element. Therefore, this is
also the value of document.getElementById(“response”).innerHTML.

ElementNode.innerHTML is a get ’n set property : it can be used either on the left or on the
right hand side of the assignment operator (“=”). Avoid using the “+=” operator with
innerHTML : when specifying to the browser what HTML has to be displayed in chunk size, the
serialization of that html, by the browser, is not always done properly. It’s like you’re talking and
pausing mid-sentence. Avoid it altogether, replace the content of an element, don’t add to it.
var myDivContentHTML = document.getElementById("response").innerHTML;
‘<p>Welcome to <span class=“title”>My Life</span>.</p>’ has been assigned to myDivContentHTML.

var myNewDivContentHTML = '<p>Welcome to <span class="title">';


myNewDivContentHTML += 'My Life</span>, me &amp; my crappy sex life.</p>';
document.getElementById("response").innerHTML = myNewDivContentHTML;
We are “building” a string in stages then assigning it to the innerHTML property.

When setting ElementNode.innerHTML, we have to use HTML entities for these five characters,
as well as for other special characters such as “™” :
&lt; &#60; Less-than sign (<)
important!
Goes without saying that we’re not using
&gt; &#62; Greater-than sign (>)
HTML entities for markup. The HTML
&amp; &#38; Ampersand (&) entities are there to avoid confusion WITH
&apos; &#39; Apostrophe or single quote (’) the markup. By “other special characters”
we mean, generally, characters that you
&quot; &#34; Quote or double quote (") cannot find on your keyboard.
You can use either entity notation.

z 3 Z Bchill

Hard talk.

?
Is it true that innerHTML is useful for moving large chunks of html from one place on
the page to another ?
FALSE. It is a monoparental world : use replaceChild(). The whole shebang will follow.
(That is : all descendant nodes will be moved.)

?
Is it true that innerHTML is useful for creating a paragraph with a lot of styling in it and
special characters ?
MAYBE TRUE. With innerHTML, our code is less verbose : we avoid long sequences of
createElement(), setAttribute() and appendChild() : in one assignment, we create and insert all
descendant elements and set their attributes. Concerning special characters, we may be more
familiar with HTML entities, than with Unicode “entities”. ElementNode.innerHTML needs
HTML entities while createTextNode() needs Unicode escape sequences. You can use this nifty
applet to look up both HTML entities and Unicode sequences : SlayerOffice Unicode Lookup.

Your mission :

You want this to appear on the page, and you are right here.
* You will add this dynamically inside a <div> on the page.
scripts.js
With this Fisher Price® vintage phone, you will be
able to contact your spirit guides. This item is sold
9.99 £ 7.99 £
In the stylesheet a class called “sale” is defined for
red-colored text, and another called “strikethrough”
is defined for text that is crossed out (in red!).
This image is phone.jpg and it’s in the images folder in the root
directory of the web site.
You will use two methods : with innerHTML and sans.
myButton.onclick = response;
..... When someone presses a button, the text must appear in a <div>,
function response() { already on the page, that has an id attribute set to “response”.
Whatever is the content of that div, you have to overwrite it. Your
............
mission is to write the code that goes here. Using one method, then
} using the other.
Answers are on the next page.

z 37 Z Bchill
Solution without innerHTML Absolutely more verbose ! But -approved
Let’s start with the perhaps fastidious way of displaying our item to be sold.
We start by getting a handle
var myDiv = document.getElementById("response"); to the <div> and emptying
while (myDiv.hasChildNodes()) { its content. Note that
hasChildNodes() is a method,
myDiv.removeChild(myDiv.firstChild); not a property. It has
} parenthesis !

var myImage = document.createElement("img"); Here we create the <img>


myImage.setAttribute("src", "images/phone.jpg"); element.
myImage.setAttribute("alt", "Fisher Price Phone"); If we were to create paragraphs with lots
of the same attributes, we’d create only
var paragraph1 = document.createElement("p"); one, set its attributes then clone it !
var paragraph2 = document.createElement("p"); <span> is an inline element in which we
var span1 = document.createElement("span");
put text (or an inline element such as
an <img />), it’s used to give special
var span2 = document.createElement("span"); stylin’ or an id “hook”.
var myText = "With this Fisher Price\u00ae vintage phone, you will be";
myText += " able to contact your spirit guides. This item is sold ";
var text1 = document.createTextNode(myText);
var text2 = document.createTextNode("9.99 \u00a3"); We are using \uXXXX
Unicode escape sequences
var text3 = document.createTextNode(" 7.99 \u00a3"); with createTextNode().
span1.className = "strikethrough"; We use HTML entities for
span2.className = "sale"; the same characters in our
With innerHTML innerHTML solution.
myDiv.appendChild(paragraph1);
paragraph1.appendChild(text1); myNewHTML = '<p> With this Fisher
Price&#174; vintage phone, you will be
paragraph1.appendChild(span1); able to contact your spirit guides. This
span1.appendChild(text2); item is sold <span class="strikethroug
paragraph1.appendChild(span2); h">9.99 &#163;</span><span class="sale">
7.99 &#163;</span></p><p><img
span2.appendChild(text3); src="images/phone.jpg" alt="Fisher Price
Phone" /></p>';
myDiv.appendChild(paragraph2); document.getElementById("response").
paragraph2.appendChild(myImage); innerHTML = myNewHTML;

z 38 Z Bchill
S erver-side DOM

Rewind. We said on page 2 that the Document Object Model is not part of JavaScript but a
separate entity existing outside of it. We said that other scripting languages can access the DOM
tree as well, such as Perl, PHP, Python and Ruby. What did we mean by this ?  Let’s explore this
question, and let us use PHP as an example of another language.
A file with the .php extension can be one big script that starts with <?php and ends with ?>
— or does not end with ?>, see http://ca.php.net/basic-syntax.instruction-separation — or it can
be an HTML document with one or more embedded PHP tags. We can generate HTML with
print, echo or printf statements enclosed in the PHP code. Echo, print, and printf are the
functions most commonly used to generate HTML content dynamically. But here is a fact much less
known : with PHP, we can use DOM scripting to generate markup. To do that, we use an
extension that comes bundled with php 5 and which is enabled by default : the DOM extension.
It replaces the DOMXML extension that was bundled with php 4. Both extensions are somewhat
similar, but the DOM extension is less buggy, truly object-oriented and it supports both the DOM
level one and two standards.
With the DOM extension, we can create a document by instantiating a DomDocument object,
using the object-oriented programming keyword new. DomDocument is a true PHP class. Its
methods and properties provide us with a toolkit of sorts, allowing us to create a document
object and add XML or XHTML elements to it, as well as attributes to these elements. The
DomDocument() constructor takes two parameters : the first is a string indicating the XML
version to be used (1.0 or 1.1), and the second is an optional parameter that tells us which type
of character encoding is used in the document. These two parameters provide the content of the
<?xml ?> declaration which although not strictly required should be found at the beginning of any
*.xml file. For example :

<?xml version="1.0" encoding="utf-8" ?>

“ XML ”, you’re saying ? It depends on how you save the document, and that’s the beauty of
it : with the DOM extension, you can either create an HTML or XML document from the
same DomDocument object, it depends on how you save that document, that is, it depends
on whether you call saveHTML() or saveXML() on the DomDocument object after its creation
and construction. For example, the following code generates markup for an html document that
contains a <h1> header in its body that says Hi !  :

<?php
$doc = new DomDocument('1.0', 'utf-8');
$html = $doc->createElement("html"); PHP is our language so we use
the “->” notation to access
$doc->appendChild($html); methods and properties of
$body = $doc->createElement("body"); objects, not the dot syntax.
$html->appendChild($body);

z 39 Z Bchill
$h1 = $doc->createElement("h1");
$salutation = $doc->createTextNode("Hi !");
$body->appendChild($h1);
$h1->appendChild($salutation);
echo $doc->saveHTML();
?>

It is the echo statement that generates the HTML markup. Something very important to
understand here is that the DOM extension is used to generate markup, no more no less. This
markup becomes a static HTML page, that is, it becomes text which the server sends back to the
browser. The browser is then responsible for rebuilding a DOM tree, from that markup, a DOM
tree that we can later manipulate with Javascript.
The above PHP code, once parsed, produces the following HTML markup :

<html><body><h1>Hi !</h1></body></html>

From it, the browser displays the following salutation :

We are far from having produced an XHTML document that will validate as strict XHTML of
the 1.0 flavor, of course. We have no DOCTYPE and no head ! But we can see something in the
browser.
We may also use the PHP DOM extension to load and parse HTML and XML documents. By
documents, we mean Document objects extracted from files or received from asynchronous Ajax
HTTP requests.

z 0 Z Bchill
D om scripting best practices

« My web page degrades gracefully. » Translation : my web page still works with
TRAIN
YOURSELF

Javascript disabled in the browser. « My Javascript is unobtrusive » : there is no


Javascript in my HTML. « My web page is backward compatible » : my page pretty much works in
older browsers. We’ll look at guidelines that support these 3 principles—which we’ll dub GD (for
graceful degradation), UJ (for unobtrusive Javascript) and BC (for backward compatibility).

Use the <noscript> element for Javascript-disabled browser. GD, BC


1
We really don’t have to, but if we want to tell people who have switched off Javascript in their browser
what they’re missing (not in these words) while visiting our web site, we do so between these tags :
<noscript> … </noscript>. The <noscript> element belongs to the <body> of the HTML file, not the
<head>, and its content can be any HTML. We may provide in that content a link to an alternative
page using an anchor element. <noscript> is an inline element : in order for our page to validate as
strict HTML 4.01 or XHTML, the <noscript> element has to be put inside a block element, and not
be directly a child of <body>. (Feed your <body> only block elements.) We can have many <noscript>
elements on the page, and each one can have an id and/or class attribute for CSS styling.

Use object detection (feature sensing), not browser sniffing. GD, BC


2
Who wants to keep track of which browser supports what ? No, you don’t. Javascript object detection
is done in the following way :
function myDOMScriptingfunction() {
if (!document.createElement) return true;
// my DOM scripting...
}
We could have checked if another other method was supported — or performed a combination
of checks. There are no opening and closing parenthesis after createElement because we are not
checking the return value of that method. We are simply verifying if it is invokable. (See guideline
# 6 and the next exercise for code examples.) If the method is not supported, then our if statement
evaluates to true, and we’re exiting the function. The “return true;” statement is often preferred
to “return false;” for this reason : most often than not, DOM scripting functions are assigned to
event handler properties (onclick, onload, onsubmit) and setting an event handler to true allows the
browser to perform its default action, while setting the event handler to false cancels that default
action. If we set the event handler of an element to a function that cannot accomplish its goal, we’d
rather have the browser perform its default action. For example, in the case of an event handler added
to a link, “return true;” allows the browser to follow the link specified by the href attribute in the
usual way. For a submit button, “return true;” allows the browser to submit the data to the server.

z 1 Z Bchill
If DOM is critical and not supported, redirect to a page that says so. GD, BC
3
Redirection can be done in many ways. Here are two ways that produce slightly different results :
Without history : if you don’t want the page from which you are redirecting to be indexed in the
history, i.e. accessible by pressing the BACK button, use location.replace(String url);
With history : if you want the current page indexed, use location.href = url;

Keep content, behavior and presentation separate. UJ


4
Create an external CSS file for the presentation and an external Javascript file for the behavior, and

5
link them to the (X)HTML document — using <link> and <script> tags.

Add to a class or use and id rather than “style”

Add an element to a predefined class (or use an id) rather than using the HTML DOM property
ElementNode.style. When we’ll want to change our style, we will simply edit our *.css file.

Your event handler should be a Node property, not an HTML attribute


6
That’s feature sensing, see Guideline # 2.

UJ

Instead of adding an inline event handler to an element in the HTML markup, we add that element to a
class, or set its id attribute. We plant our hooks ! Once the page is loaded, we may add all our event
handlers, and tell them what they have to do. Example :
window.onload = init; * We run our init() function when the the DOM tree is built.
function init() { ...
var myLink = document.getElementById("myPopUpLink"); We used an “id” as hook.
myLink.onclick = function() { We could have defined our function some place else,
if (!this.getAttributeNode) return true; just like we did with init :
window.open(this.getAttributeNode("href").nodeValue,"info");
return false;
} ... myLink.onclick = response; *
} function response() {
We are assigning a function to the if (!this.getAttribute) return true;
property “onclick” of the document window.open(this.getAttribute("href"),"info");
element “myLink”. Hence, the “this” return false;
keyword refers to myLink. }

* We are assigning the function itself, not the result of invoking the function.

z 42 Z Bchill

Hard talk about unobtrusiveness


QUESTION : Isn’t it anal to avoid at all cost putting Javascript code in the HTML file,
to the point of not using event-handler attributes like onclick inside the HTML ?
ANSWER : Nope. Strictly unobtrusive Javascript usually means that the page will
degrade gracefully — in plain English, the page will still “work” if Javascript is turned off
in the browser. OK, with event-handler attributes, the page may still degrade gracefully.
But being strict is easy — and made easier if we add hooks on the page (but we can do without). A
hook can be a class attribute for all elements that will share the same event handler, or an id attribute
for one element that we’ll use a particular event handler for. It is easy to add Javascript code in our
external *.js file to register event handlers on all document elements that need them.
Let’s use the example of a form. We may want to perform some client-side validation before we
submit the form to the server. Here’s the form’s markup in the HTML file :
<form class="submit-form" action="process.php" method="POST">
..... <input type="submit" /> .....
</form>
We could add an onsubmit attribute that would call a validateFields() Javascript function :
<form class="submit-form" action="process.php" method="POST" onsubmit="return validateFields();">
But let’s use unobtrusive Javascript instead. First, we’ll add a hook (although we could do without) :
<form id="form1" class="submit-form" action="process.php" method="POST">
In the external Javascript file, we make sure to add our event handlers when the DOM tree is built :
window.onload = init;
In the init() function, we assign a function to the onsubmit property of the form element.
if (!document.getElementById) return true; If the form is valid, we can alternatively
var myForm = document.getElementById("form1"); submit the form in our code. Instead of
myForm.onsubmit = function() { typing this : “return true;” we’d type that :
if (validateFields()) { “this.submit(); return false;”.
return true; 
} else { * We can and SHOULD replace the whole shebang with :
return false;  myForm.onsubmit = validateFields;
}
}
The function validateFields() will use DOM scripting to tell the user what’s wrong with his form, if
there’s anything wrong with it. The function will return true if the form is valid and false if it’s not.
If validateFields() returns true, the browser must perform its default action for the element, so we
set the onsubmit property to true . If there is something wrong with the form, we must prevent
the browser from performing its default action, hence we set the onsubmit property to false .

z 43 Z Bchill
I
nternet Explorer 7 preliminary bug report : the onclick event-handler

Internet Explorer 7 is quite the improvement compared to IE6,


but it suffers a few regressions. One of these regressions is that It looks like the
we cannot, using Javascript, cancel the browser’s default action FINAL 7.0.5730.11 version
for an anchor element by setting this element’s onclick event- RELEASE of IE7 IS the final
handler to false. There are many ways to skin a cat : one way release.
to solve the problem is to remove the href attribute, yet let the
cursor that hovers over the element remain pointy, using CSS.
Let us consider the following HTML markup :
<a id="myLink" href="http://www.myWebSite.com">my web site</a>

We have planted our hook.


We must register our event-handlers when the web page is loaded. In this example, we’ll use a
function called registerEventHandlers() — an arbitrary name :

window.onload = registerEventHandlers;

In the function’s definition, we do three things. First , we add the anchor element to a class. The
goal is to change the aspect of the cursor. The CSS rule would be, as defined in our stylesheet :

.clickable { cursor:pointer; } /* Rule for the class clickable */

In registerEventHandlers(), we either set the property className, or we go “the long way” :


var myLink = document.getElementById("myLink");
var classAttr = myLink.getAttributeNode("class");
if (classAttr) classAttr.nodeValue += " clickable";
else {
var newAttr = document.createAttribute("class");
newAttr.nodeValue = "clickable";
myLink.setAttributeNode(newAttr);
}
Secondly  , we get rid of the href attribute — that is, if we don’t want the browser to follow
the link in the usual way after it’s done executing all code inside doThis() (see below). Finally  ,
we register the onclick event-handler for the anchor element (myLink) :
myLink.removeAttribute("href");  // We first note the value if we need it
myLink.onclick = doThis; 
We can also use another method here :
myLink.removeAttributeNode(myLink.getAttributeNode(“href”));

z  Z Bchill
Ajax Exercise : no Ajax competence required
We have one form on our web page. As it is now, when someone submits that form to the server,
process.php is run, and a brand new HTML page is returned and loaded in the browser. The new
page looks exactly the same, except for this : a text value we’ll refer to as the “balance” has changed.
Now, given that the browser is able to accommodate us, we want to submit the same form through an
asynchronous HTML request. We want the server to run an alternate script, process-ajax.php,
and return to the browser one value that we will display on the web page using DOM scripting. We
want to update the page with that new value without reloading said page ! We will use unobtrusive
Javascript, and make sure our web page degrades gracefully. We are not changing anything in our
HTML, except this : we are using a <span> element to contain the text value to be updated on the
page, and we’ve set the id attribute of that element to “balance”. Fill in the blanks in script.js :

var myHttpRequest = null; // will contain a handle to my HTTP request


function createXMLHttpRequest() { ... } // Some code we need not worry about
window.onload = ; // To do as soon as the DOM tree is built
function initialization() {
if (!document.getElementsByTagName) return true; // Feature sensing...
var myForm = ;
myForm.onsubmit = ;
}
w e wa nt t o get the n ew valu e fo r “ balance” asynchronously
function getBalance() {
myHttpRequest = createXMLHttpRequest();
if (!myHttpRequest) ; // if we can’t use Ajax...
var url = "process-ajax.php"; // my AJAX php scrip
......
myHttpRequest.onreadystatechange = handleResponse; // set callback function
myHttpRequest.send(null); // we are sending here the HTML request and...
; // ... preventing the default form submit
} row ser

.
e b
function handleResponse() { This gets executed when the server sends back a response to th
......
var newValueReturnedByServer = myHttpRequest.responseText;
var responseField = document.getElementById(" ");
responseField.firstChild.nodeValue = newValueReturnedByServer;
......
}
return false initialization()
return true
span initialization

document.getElementsByTagName("form")[0] getBalance
balance getBalance()

— return true — return false — balance


Answers : initialization — document.getElementsByTagName("form")[0] — getBalance

z 5 Z Bchill
Final Exam. No computer, no books.
Print pages 46-48, and give yourself an hour. There are only 15 questions.

1. True or false.
Attributes of the same element are represented by sibling Attr Nodes in the DOM tree.

2. Select the right answer.


Although the W3C DOM API is language-neutral and the DOM tree exists as an entity outside
of the language used to acess and modify it, Javascript is usually the prefered language to modify
the (X)HTML Document object because…
A. The browser builds the DOM tree. C. both A and B.
B. Javascript is a client-side scripting language. D. none of the above.

3. True or false.
In XHTML and XML, an empty element is a self-closing element. Its markup is :
<tagName attr1="value1" attr2="value2" />

. True or false.
Using the HTMLElement style property is hardly ever recommended. However, it is the only
property that can help us animate blocks on the page — or alternatively prevent some blocks to
move when the user is scrolling the web page in browsers that do not support fixed positioning.

5. Select the right answer.


When the user clicks to submit a form on the web page, we want to perform an action, i.e. call
the function firstAction(). If that first action is successful, we then want to perform a second
action, i.e. call secondAction(). If and only if both actions are successful, if they both return true,
we then want to let the browser perform its default action, which is to submit the form to the
server. How do we go about it ? When the DOM tree is built…
A. myForm.onsubmit = firstAction; C. both A and B
myForm.onsubmit = secondAction;
B. myForm.onsubmit = function() { D. none of the above
return (firstAction() && secondAction());
}

z  Z Bchill
. Select the right answers. Multiple Choices are allowed.
These are ways to append a Node (newNode) to another Node (its future parent, parentNode) :
A. parentNode.appendChild(newNode);
B. parentNode.childNodes.push(newNode);
C. parentNode.lastChild = newNode;
D. parentNode.insertBefore(newNode, null);
E. parentNode.replaceChild(newNode, null);

7. Select the right answer.


The proper way to check if the DOM API is implemented in the browser — and how well it is
supported — is to use…
A. Browser sniffing C. both A and B.
B. Feature sensing, also known as object detection D. none of the above.

8. True or false.
When trying to set the value of an Attr Node when such node does not exist — when the
attribute name is valid for the element but either hasn’t been added yet or is not set to a default
value by the browser — the following code will never work because getAttributeNode(attrName)
will return a null value.
someElement.getAttributeNode(attrName).value = attrValue;

. True or false.
The following code effectively removes the element from the web page, leaving no space behind.
someBlockElement.style.visibility = "hidden";

10. True or false.


If we want our Javascript to be unobtrusive, and we want to do DOM scripting when a certain
event is “captured” by the browser, we have to assign a function to the HTMLElement property
of the Element Node, property associated with that event, for example onclick or onsubmit,
and we have to do so in a separate *.js file to which our HTML page will link. Additionally,
we have to assign this function when the DOM tree is built, that is when the onload event is
captured on the window object. That is what is meant by registering an event handler as a node
property rather than as an attribute in the HTML page.

z 7 Z Bchill
11. True or false.
Line returns and tabs that are used to format the markup in an html file are treated like a
single space by browsers. Also, consecutive whitespaces are typically treated like a single blank
character. From these, Firefox creates Text Nodes containing a blank character, both in head and
body of the Document tree.

12. Coding question.


Given that we have two Element Nodes, oldParent and newParent, and that we’d wish to transfer
the entire content (as is) of oldParent into newParent, which has no content, how would we go
about it ? (After this operation, oldParent must be a leaf node.) Hint : read question no 13 before
you start working on this question.

13. Coding question.


Given that we have two Element Nodes, oldParent and newParent, and that we’d wish to transfer
the entire content (as is) of oldParent into newParent, and given that oldParent is the only child of
newParent, how would we go about it ? (After this operation, oldParent must be a leaf node.) Hint :
the answer you give here may be the exact same answer you give for question 12.

1. True or false.


In the following code, we are empting the content of the <body> element of a web page.
var bodyEl = document.documentElement.childNodes[1];

for (var i=0; i < bodyEl.childNodes.length; i++) {

bodyEl.removeChild(bodyEl.childNodes[i]);

15. Select the right answer.


The following properties can be set on an Element Node.
A. el.nodeType D. A, B and C
B. el.nodeName E. B and C
C. el.nodeValue F. none of the above

z 8 Z Bchill
Final Exam. No computer, no books.
Answers. If you get 10 out of 15, you are good. Twelve out of 15, pat yourself on the back. If you
get 13, 14 or even 15, you are an extraordinaire DOM scribe.

1. True or false.
Attributes of the same element are represented by sibling Attr Nodes in the DOM tree. False.
Attr Nodes are not attached to the DOM tree, they have no parent, and they have no sibling.

2. Select the right answer.


Although the W3C DOM API is language-neutral and the DOM tree exists as an entity outside
of the language used to acess and modify it, Javascript is usually the prefered language to modify
the (X)HTML Document object because… The answer is C. Javascript is preferred because it is
the browser that builds the DOM tree and Javascript is a language understood by browsers.

3. True or false.
In XHTML and XML, an empty element is a self-closing element. Its markup is :
<tagName attr1="value1" attr2="value2" /> True, and an empty element never has children.
Whereas a nonempty element may or may not have content, hence have zero, 1 or serveral child nodes.

. True or false.
Using the HTMLElement style property is hardly ever recommended. However, it is the only
property that can help us animate blocks on the page — or alternatively prevent some blocks to
move when the user is scrolling the web page in browsers that do not support fixed positioning.
True. Note : fixed positioning (fi xed with regards to the window) is supported in IE 7 (it wasn’t in IE 6).

5. Select the right answer.


When the user clicks to submit a form on the web page, we want to perform an action, i.e. call
the function firstAction(). If that first action is successful, we then want to perform a second
action, i.e. call secondAction(). If and only if both actions are successful, if they both return true,
we then want to let the browser perform its default action, which is to submit the form to the
server. The answer is B (see below). Every time we “register” a DOM Level 0 event handler,
such as onload, onclick, onsubmit, onchange, we overwrite any previously assigned handler for that
event. Hence with answer A, the browser will only invoke secondAction() then performs its (the
browser’s) default action. About the behavior of the && operator in correct answer B : it starts
by evaluating the first operand (the expression on the left), hence firstAction() is invoked. If the
value returned by that function is false, the operator returns false, and secondAction() is not
called. Otherwise, && evaluates the second operand, i.e. invokes secondAction(), and returns the
value returned by that function, hence both functions are invoked.
B. myForm.onsubmit = function() { return (firstAction() && secondAction()); }

z  Z Bchill
. Select the right answers. Multiple Choices are allowed.
These are ways to append a Node (newNode) to another Node (its future parent, parentNode) :
A. parentNode.appendChild(newNode); Perfect.
B. parentNode.childNodes.push(newNode); childNodes is a read-only array.
C. parentNode.lastChild = newNode; lastChild is a read-only property.
D. parentNode.insertBefore(newNode, null); Yes, that will work.
E. parentNode.replaceChild(newNode, null); The 2nd argument has to be a valid child Node.

7. Select the right answer.


The proper way to check if the DOM API is implemented in the browser — and how well it is
supported — is to use… feature sensing, a.k.a. object detection. B is the correct answer.
8. True or false.
When trying to set the value of an Attr Node when such node does not exist — when the
attribute name is valid for the element but either hasn’t been added yet or is not set to a default
value by the browser — the following code will never work because getAttributeNode(attrName)
will return a null value.
someElement.getAttributeNode(attrName).value = attrValue; True. When the
attribute does not exist, we must create it either using 1. createAttribute-then-set-the-Attr-Node-
nodeValue-then-use-setAttributeNode, OR 2. use SetAttribute(attrName, attrValue), OR 3. with
“class” and “for” attributes, use respectively the properties className and htmlFor.

. True or false.
The following code effectively removes the element from the web page, leaving no space behind.
someBlockElement.style.visibility = "hidden"; False. To do “as if” the element has
been removed, to fake it, we add someBlockElement to a class, the definition of which has to be
{ display:none; } To effectively remove the element, we simply use the method removeChild().

10. True or false.


If we want our Javascript to be unobtrusive, and we want to do DOM scripting when a certain
event is “captured” by the browser, we have to assign a function to the HTMLElement property
of the Element Node, property associated with that event, for example onclick or onsubmit,
and we have to do so in a separate *.js file to which our HTML page will link. Additionally,
we have to assign this function when the DOM tree is built, that is when the onload event is
captured on the window object. That is what is meant by registering an event handler as a node
property rather than as an attribute in the HTML page. Complicated to read but true.

z 50 Z Bchill
11. True or false.
Line returns and tabs that are used to format the markup in an html file are treated like a
single space by browsers. Also, consecutive whitespaces are typically treated like a single blank
character. From these, Firefox creates Text Nodes containing a blank character, both in head and
body of the Document tree. True. However, we can safely rely on the document.documentElement
having always 2 child Nodes, the 1st one being the head, and the 2nd being the body, fortunately. No Text
Nodes snugged up before or in between these elements, ever, whatever formatting we give our markup.

12-13. Coding question.


Given that we have two Element Nodes, oldParent and newParent, and that we’d wish to transfer
the entire content (as is) of oldParent into newParent, which has no content, how would we go
about it ? (After this operation, oldParent must be a leaf node.) Hint : read question no 13 before
you start working on this question. Question no 13 says : « given that oldParent is the only child of
newParent ». Here is a solution that will work flawlessly in both situations :
while (oldParent.hasChildNodes()) {
newParent.insertBefore(oldParent.lastChild, newParent.firstChild);
} We want to preserve the order in which the child Nodes appear on the page. If
oldParent is not a child of newParent, newParent.firstChild is null in the first
iteration, which is totally fine. A null value means that insertBefore() will behave
like append(). We move the nodes starting with the last one…
1. True or false.
In the following code, we are empting the content of the <body> element of a web page.
var bodyEl = document.documentElement.childNodes[1];
for (var i=0; i < bodyEl.childNodes.length; i++) {
bodyEl.removeChild(bodyEl.childNodes[i]);

} False. Node.childNodes is a dynamic property that always reflect the actual content of
Node, hence childNodes.length changes with each iteration through the for loop. When
the body has 2-3 child nodes, we’re stuck with the last one; when it has 4-5, we’re stuck
with the last 2, etc. We can successfully remove all child Nodes from the body like this :
while (bodyEl.hasChildNodes()) { bodyEl.removeChild(bodyEl.firstChild); }

15. Select the right answer.


The following properties can be set on an Element Node. The answer is F : neither nodeType,
nor nodeName, nor nodeValue, can be set on an Element Node. nodeValue is a property of
the Node object that is not read-only. Element Nodes, as do other types of nodes, inherit
the properties and methods of the Node object. However, if we try setting the nodeValue of
an Element Node, we are unable to change it, it remains “null”. No error shows up in the
browser’s console. The setting fails silently. nodeType and nodeName are read-only for all Nodes.

z 51 Z Bchill
M
ore : recommended reading

The fifth edition of JavaScript: The Definitive Guide


(O’Reilly Media, August 1st , 2006) by sweetheart
David Flanagan (I don’t know him, he just seems sweet)
provides an excellent coverage of the W3C DOM. If
you buy this 994 pages book, you get free access to its
digital version on Safari for 45 days. The access code
is in the book, in the last pages. Besides asking you for
that code, the web site prompts you with a question :
what is the last word of section whichever. The book is
remarkably useful and totally worth buying, even for
destitute people like me. I get some of the food I eat
from a charitable organization. No kidding.

In my humble opinion, another nearly indispensable book for


people who are into web development is the just as thick volume
Web Design in a Nutshell (O’Reilly Media, February, 2006). The
third edition has been thoroughly updated and expanded. The
book is authored by Jennifer Niederst Robbins. Aaron Gustafson
contributed the sections on DOM scripting and Javascript, that is
Part IV. The Behavioral Layer: JavaScript and the DOM,
and that part of the book is remarkably clear, concise and yet
covers so, so much. On Amazon.com, Aaron writes : « I tried to
keep these chapters both engaging and informative. I hope you
enjoy reading them as much as I enjoyed writing them. » I did. This
book comes highly recommended. (Only the CSS section sucks.)

As far as Ajax goes, I recommend a book that may cause some to


raise an eyebrow : Sams Teach Yourself AJAX in 10 Minutes (Sams,
April 28, 2006). Not so long ago, I thought that the books in that
series could be read in ten minutes. Not so : these books rather
apply chunking as an educational « design » tool : each section of
the book can be read and digested in 10 minutes. (That translates
into 20 minutes for me.) Phil Ballard is a master at breaking things
down and is making the whole subject of Ajax simple. To be fair,
Ajax is simple. Unfortunately, it is usually — almost always — made
into a sophisticated subject by winded authors, so sophisticated
that one would require libraries made by others and a knowledge of
design patterns, specific to Ajax, to simplify the task of doing Ajax.
It’s propaganda. Ajax is easy. Read that book. And then read other
books. Then laugh. Then create your own “libraries”.

z 52 Z Bchill
C redits

This book was written in open-source FreeMind and designed in expensive InDesign CS.
FreeMind is a free clustering software written in Java, and it is an amazing tool. I love it.

The font used here is Gill sans.

This funky handwriting is by Chank, and it is called Skippy Sharp.


And that incredibly ºleek cursive font is
Shimmer and it is by Blue Vinyl Fonts.
This is Happy and it is by Canada Type.
Images have been picked here and there, mostly from catalogs and magazines.
I would like to thank my mother and father who know nothing of what I do but support me in all
that I do, although not financially. They trust that somehow someday I will earn a living doing what
I love, preferably before the age of 40, or they hope that I will get myself on welfare. I do not yet
regret having ditched whatever I was doing six years ago. I would not go back.
No thanks to my G420 Sony CRT monitor whose contrast has been gone for a while now, which
makes working at the compute quite difficult. No thanks to my hard drive failure during the
summer of 2006, and subsequent hair loss.
Thank you for having downoaded this ebook.
Thanks to these writers : Augusten Burroughs, Philip K. Dick, and Kurt Vonnegut. Thank you
providence and conscientious creation. Thank you mindless fun.
Copyright © 2006-2007 by Caroline Hill.
All rights reserved. No part of this work may be reproduced or transmitted in any form or by
any means, electronic or mechanical, including photocopying, or by any information storage or
retrieval system, without the prior written permission of the copyright owner. The source code
for this book is freely available at http://www.11heavens.com/new-DOM-scripting-book.

z 53 Z Bchill

You might also like