Parents, children and siblings...

I'll show you features you can't refuse.

Now that we have covered basic and advanced features of the JavaScript language, it's time to move on. We're going to take advantage of our main platform : the browser. We'll learn how to interact with HTML and CSS to make dynamic pages.

Interacting with a webpage and its resources is done using a tree. It's a bit like a family tree. Some nodes representing HTML tags are linked as parents, children, brothers and sisters. We'll also see how this tree can be manipulated especially with dollars.

Definition of the DOM

DOM stands for Document Object Model. It is an API that allows access for HTML and XML documents. This API provides a representation of the document and ways to retrieve and manipulate it. It is cross-platform and language-independant. The DOM is a standard maintained by W3C.

The DOM specification has evolved since it's standardization. The current version/level is 3. See links for more informations...

This course is about Web so we'll address JavaScript's implementation but remember that a lot of languages have a DOM implementation.

When a navigator display a web page, the rendering engine parses the HTML code to turn the tags to DOM nodes in a tree called the content tree. After that, the rendering engine takes care of style (external CSS files and style elements) to create the render tree. Then rendering finishes its task with 2 steps : layout and painting. See links for more informations...

Tree representation

Nodes

Definition

In an HTML document, everything is represented as a Node object. The different nodes are organized in the DOM tree with parent/children/sibling relationships.

Types

There are different types of nodes. The HTML document itself is the root node and is a property of the global window object. Every HTML element is represented by a node. Note that text between or inside HTML elements is considered as text nodes. Attributes set on HTML tags are also considered as nodes. Node types are listed as properties on the Node object and have an integer value. Here are some examples :

Properties

On each node, you can access various properties to obtain more informations on the node. Here are a few examples...

nodeType

It contains an integer value corresponding to one of the properties of the Node object.

var doc = document;              // direct access to window property
console.log(doc.nodeType);       // => 9
console.log(Node.DOCUMENT_NODE); // => 9
nodeName

It contains a string value. If the node represents an HTML element, its nodeName is the tag name in uppercase. Example : DIV, A ... For text nodes it returns #text. See reference for more details.

<div id="vito" class="sicilian">Vito Andolini (Corleone)</div>
<!-- Corleone is his origin village -->
console.log(document.nodeName); // => '#document'
console.log(elt.nodeName);      // => 'DIV'
console.log(txt.nodeName);      // => '#text'
console.log(com.nodeName);      // => '#comment'
console.log(att.nodeName);      // => 'id'
nodeValue

Contains the textual value of the node. Element nodes (and the document node) doesn't have a nodeValue. Their text contents can be found in one of their children text nodes.

<div id="vito" class="sicilian">Vito Andolini (Corleone)</div>
<!-- Corleone is his origin village -->
console.log(document.nodeValue); // => null
console.log(div.nodeValue);      // => null
console.log(txt.nodeValue);      // => 'Vito Andolini (Corleone)'
console.log(com.nodeValue);      // => ' Corleone is his origin village '
console.log(att.nodeValue);      // => 'vito'
attributes

Excepted for text nodes, every node can have attributes. It's a collection of attribute nodes and can be iterated like an array with a for loop and using its length property.

<div id="vito" class="sicilian">Vito Andolini (Corleone)</div>
<!-- Corleone is his origin village -->
console.log(document.attributes);         // => null
console.log(div.attributes.length);       // => 2
console.log(div.attributes[1].nodeName);  // => 'class'
console.log(div.attributes[1].nodeValue); // => 'sicilian'
console.log(txt.attributes);              // => null
console.log(com.attributes);              // => null
console.log(att.attributes);              // => null

Tree Example : HTML

Let's see and example of an HTML document and its DOM tree equivalent.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Corleone Family : HTML to JSON</title>
  </head>
  <body>
    <h1>Corleone Family</h1>
    <div id="vito" class="sicilian">Vito Andolini (Corleone)
      <div id="santino">Santino ("Sonny") Corleone</div>
      <div id="frederico">Frederico ("Fredo") Corleone</div>
      <div id="michael">Michael Corleone</div>
      <div id="constanzia">Constanzia ("Connie") Corleone</div>
    </div>
  </body>
</html>

Tree Example : DOM Tree

#document DOCUMENT_NODE html HTML ELEMENT_NODE DOCUMENT_TYPE_NODE HEAD BODY TEXT_NODE(with whitespaces) META TITLE #text TEXT_NODE DIV H1 id ATTRIBUTE_NODE class #text DIV DIV DIV DIV #text id id id id #text #text #text #text

Family properties

In order to traverse the document, you have a lot of "family" properties. Here's a few examples. See reference for more details.

Be careful, these properties take the text node into account.

var doctype = document.firstChild;
var html = doctype.nextSibling;        // previousSibling also exists
var body = html.lastChild;
var h1 = body.firstChild;
console.log(h1.nodeName);              // => "#text"  :-(
h1 = h1.nextSibling;
console.log(h1.nodeName);              // => "H1"     :-)
console.log(h1.parentNode === body);   // => true
console.log(h1.parentNode === html);   // => false
var vito = h1.nextSibling.nextSibling; // skip "empty" text nodes
console.log(vito.childNodes.length);   // => 9 !!!
// 1 text node + 4 div nodes + 4 "empty" text nodes = 9 children

Family properties (elements)

It's often annoying to retrieve the texts nodes when traversing the DOM tree. Classic nodes are more interesting! Some properties exists to ease that.

var vito = document.lastChild.lastChild.lastElementChild;
// document => <html> => <body> => <div id="vito">
var children = vito.children;                    // live list!

Note that the children property is live. The children variables points to something that is always synced with DOM tree.

var santino = vito.firstElementChild;
console.log(santino === children[0]);            // => true
var frederico = santino.nextElementSibling;
console.log(frederico === children[1]);          // => true
var constanzia = vito.lastElementChild;
console.log(constanzia === children[3]);         // => true
var michael = constanzia.previousElementSibling;
console.log(michael === children[2]);            // => true

Search methods

Better than traversing the DOM tree, direct search is faster and simpler to use in most cases. Here's a few examples. See reference for more details.

var vito = document.getElementById('vito');
var michael = document.getElementById('michael');
console.log(vito === michael.parentNode);         // => true
var divs = document.getElementsByTagName('div');
console.log(divs.length);                         // => 5
console.log(divs[3].firstChild.nodeValue);        // => Michael Corleone
var sicilians = document.getElementsByClassName('sicilian');
console.log(sicilians[0] === vito);               // => true

Support for getElementsByClassName is not present on very old browers. Look at Can I Use? for more details.

var constanzia = document.querySelector('.sicilian #constanzia');
var herYoungerBrother = constanzia.previousSibling.previousSibling;
console.log(herYoungerBrother === michael);       // => true
var vitosChildren = document.querySelectorAll('#vito div');
console.log(vitosChildren.length);                // => 4
console.log(vitosChildren[2] === michael);        // => true

Support for querySelector and querySelectorAll is not present on old browers. Look at Can I Use? for more details.

Manipulation

Element manipulation

Now that we know how to retrieve the node we're interested in, it would be very nice to do some modifications. Here's some examples of the different properties you can use to alter the DOM. Look at reference for more details.

var michael = document.getElementById('michael');
console.log(michael.id);                    // => michael
michael.id = 'MICHAEL';                     // => id modified!
console.log(michael.innerHTML);             // => Michael Corleone
michael.innerHTML = 'Michael CORLEONE';     // => HTML content modified!
console.log(michael.hasAttribute('title')); // => false
michael.setAttribute('title', 'Michael Corleone!!');
console.log(michael.getAttribute('title')); // => Michael Corleone!!
console.log(michael.className);             // => ""
michael.classList.add('character');
michael.classList.add('male');
console.log(michael.className);             // => "character male"

Support for classlist is not present on old browers. Look at Can I Use? for more details.

Style manipulation

If you wanna manipulate the style of an element, here's some examples showing off the properties. Look at reference for more details.

var michael = document.getElementById('michael');
michael.style.paddingLeft = '10px';
michael.style.borderLeft = '5px solid #00F';
michael.style.color = 'blue';
michael.style.cssFloat = 'left';
michael.style.styleFloat = 'left'; // IE :-(

Element tree manipulation

The only thing we miss now is how to create and delete elements. We'll also see how to manipulate the different relationships (parent/child/sibling). Here's a few examples. Look at reference for more details.

var tom = document.createElement('div');
tom.innerHTML = 'Tom Hagen';
tom.id = 'tom';
tom.classList.add('adopted');
var vito = document.getElementById('vito');
vito.appendChild(tom);                          // inserts at the end
vito.removeChild(vito.children[2]);             // removes michael
var michael = vito.cloneNode(vito.children[0]); // clones santino
michael.id = 'michael';                         // replace id
michael.innerHTML = 'Michael Corleone';         // replace HTML content
vito.insertBefore(michael, vito.children[2]);   // insert before connie

jQuery & the DOM

What is jQuery ?

jQuery is a powerful JavaScript library originally created by John Resig. It has a lot of features for JS developers. For this course we'll focus on how it can help us with the DOM. jQuery aims to provide easier/simpler features and implementations of existing ones that are not supported on old browsers. They try their best to have the same behaviour and results on each browser.

The library provides a main function that also has properties. It can be accessed through two names : jQuery and the $ sign.

How it works ?

Let's use some example to understand in details how the library works. Remember that everything is based on the $ function and that it takes CSS selectors as an argument.

var michael1 = document.querySelector('#michael');
var michael2 = $('#michael'); // similar to document.querySelector
                              // but returned value has more properties
                              // and more functions
                              // works on old browsers :-D
console.log(michael1.getAttribute('id'));       // => michael
console.log(michael2.attr('id'));               // => michael
michael2.attr('id', 'MICHAEL');                 // => id modified!
console.log(michael1.innerHTML);                // => Michael Corleone
console.log(michael2.html());                   // => Michael Corleone
michael2.html('Michael CORLEONE');              // => content modified!
console.log(michael1 === michael2);             // => false
console.log(michael1 === michael2[0]);          // => true

Easier manipulation

jQuery is very efficient on parent/child/sibling manipulation. The syntax is easy to use.

Note that jQuery calls can be chained. This is pretty elegant and useful. This means you can select an element, call a function and call directly another function on the result. Look at the examples for more details.

$('#michael').remove();         // don't need parent reference
var michael = document.createElement('div');
$(michael)
  .attr('id', 'michael')        // methods calls can be chained
  .html('Michael Corleone')
  .addClass('character')
  .addClass('male')
  .insertBefore('#constanzia'); // can insert before
var children = $('#vito').children();
var santino = children.eq(0);
var siblings = santino.siblings();
var constanzia = siblings.last();
var familyOrder = children.index(constanzia);
console.log(familyOrder);                      // => 3 (4th)
var michael = constanzia.prev();

Easier CSS

The CSS manipulations are also easier. Have a look at the examples. If you need help, the reference is here...

var michael = $('michael');
michael.css('padding-left', '10px');
michael.css('border-left', '5px solid #00F');
michael.css('color', 'blue');
michael.css('float', 'left');  // Every browser