Web Programming Step by Step

Lecture 15
The DOM Tree

Reading: 8.3, 9.1

Except where otherwise noted, the contents of this presentation are Copyright 2010 Marty Stepp and Jessica Miller.

Valid XHTML 1.1 Valid CSS!

Complex DOM manipulation problems

How would we do each of the following in JavaScript code? Each involves modifying each one of a group of elements ...

The DOM tree (8.3)

DOM tree

Types of DOM nodes (8.3.1)

<p>
	This is a paragraph of text with a 
	<a href="/path/page.html">link in it</a>.
</p>
DOM Tree

Traversing the DOM tree (8.3.2 - 8.3.3)

every node's DOM object has the following properties:

name(s) description
firstChild, lastChild start/end of this node's list of children
childNodes array of all this node's children
nextSibling, previousSibling neighboring nodes with the same parent
parentNode the element that contains this node

DOM tree traversal example

<p id="foo">This is a paragraph of text with a 
	<a href="/path/to/another/page.html">link</a>.</p>
navigate tree

Element vs. text nodes

<div>
	<p>
		This is a paragraph of text with a 
		<a href="page.html">link</a>.
	</p>
</div>

Prototype's DOM element methods (9.1.3)

absolutize addClassName classNames cleanWhitespace clonePosition
cumulativeOffset cumulativeScrollOffset empty extend firstDescendant
getDimensions getHeight getOffsetParent getStyle getWidth
hasClassName hide identify insert inspect
makeClipping makePositioned match positionedOffset readAttribute
recursivelyCollect relativize remove removeClassName replace
scrollTo select setOpacity setStyle show
toggle toggleClassName undoClipping undoPositioned update
viewportOffset visible wrap writeAttribute

Prototype's DOM tree traversal methods (9.1.5)

method(s) description
ancestors, up elements above this one
childElements, descendants, down elements below this one (not text nodes)
siblings, next, nextSiblings,
previous, previousSiblings, adjacent
elements with same parent
as this one (not text nodes)
DOM element
// alter siblings of "main" that do not contain "Sun"
var sibs = $("main").siblings();
for (var i = 0; i < sibs.length; i++) {
	if (sibs[i].innerHTML.indexOf("Sun") < 0) {
		sibs[i].innerHTML += " Sunshine";
	}
}

Selecting groups of DOM objects (8.3.5)

name description
getElementsByTagName returns array of descendents with the given tag, such as "div"
getElementsByName returns array of descendents with the given name attribute (mostly useful for accessing form controls)

Getting all elements of a certain type

highlight all paragraphs in the document:

var allParas = document.getElementsByTagName("p");
for (var i = 0; i < allParas.length; i++) {
	allParas[i].style.backgroundColor = "yellow";
}
<body>
	<p>This is the first paragraph</p>
	<p>This is the second paragraph</p>
	<p>You get the idea...</p>
</body>

Combining with getElementById

highlight all paragraphs inside of the section with ID "address":

var addrParas = $("address").getElementsByTagName("p");
for (var i = 0; i < addrParas.length; i++) {
	addrParas[i].style.backgroundColor = "yellow";
}
<p>This won't be returned!</p>
<div id="address">
	<p>1234 Street</p>
	<p>Atlanta, GA</p>
</div>

Prototype's methods for selecting elements

Prototype adds methods to the document object (and all DOM element objects) for selecting groups of elements:

getElementsByClassName array of elements that use given class attribute
select array of descendants that match given CSS selector, such as "div#sidebar ul.news > li"
var gameButtons = $("game").select("button.control");
for (var i = 0; i < gameButtons.length; i++) {
	gameButtons[i].style.color = "yellow";
}

The $$ function (9.1.5)

var arrayName = $$("CSS selector");
// hide all "announcement" paragraphs in the "news" section
var paragraphs = $$("div#news p.announcement");
for (var i = 0; i < paragraphs.length; i++) {
	paragraphs[i].hide();
}

Common $$ issues

Creating new nodes (8.3.5)

name description
$(document.createElement("tag")) creates and returns a new empty DOM node representing an element of that type
document.createTextNode("text") creates and returns a text node containing given text
// create a new <h2> node
var newHeading = $(document.createElement("h2"));
newHeading.innerHTML = "This is a heading";
newHeading.style.color = "green";

Modifying the DOM tree

Every DOM element object has these methods:

name description
appendChild(node) places given node at end of this node's child list
insertBefore(newold) places the given new node in this node's child list just before old child
removeChild(node) removes given node from this node's child list
replaceChild(newold) replaces given child with new node
var p = $(document.createElement("p"));
p.innerHTML = "A paragraph!";
$("main").appendChild(p);

Removing a node from the page

function slideClick() {
	var bullets = document.getElementsByTagName("li");
	for (var i = 0; i < bullets.length; i++) {
		if (bullets[i].innerHTML.indexOf("children") >= 0) {
			bullets[i].remove();
		}
	}
}

DOM versus innerHTML hacking

Why not just code the previous example this way?

function slideClick() {
	$("thisslide").innerHTML += "<p>A paragraph!</p>";
}
  • Imagine that the new node is more complex:
    • ugly: bad style on many levels (e.g. JS code embedded within HTML)
    • error-prone: must carefully distinguish " and '
    • can only add at beginning or end, not in middle of child list
function slideClick() {
	this.innerHTML += "<p style='color: red; " +
			"margin-left: 50px;' " +
			"onclick='myOnClick();'>" +
			"A paragraph!</p>";
}

Problems with reading/changing styles

<button id="clickme">Click Me</button>
window.onload = function() {
	$("clickme").onclick = biggerFont;
};
function biggerFont() {
	var size = parseInt($("clickme").style.fontSize);
	size += 4;
	$("clickMe").style.fontSize = size + "pt";
}

Accessing styles in Prototype (9.1.4)

function biggerFont() {
	// turn text yellow and make it bigger
	var size = parseInt($("clickme").getStyle("font-size"));
	$("clickme").style.fontSize = (size + 4) + "pt";
}

Common bug: incorrect usage of existing styles

this.style.top = this.getStyle("top") + 100 + "px";            // bad!
this.style.top = parseInt(this.getStyle("top")) + 100 + "px";  // correct

Setting CSS classes in Prototype (9.1.4)

function highlightField() {
	// turn text yellow and make it bigger
	if (!$("text").hasClassName("invalid")) {
		$("text").addClassName("highlight");
	}
}