Parents, children and siblings...
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...
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 :
ELEMENT_NODE
(1)
HTML element
ATTRIBUTE_NODE
(2)
on HTML elements
TEXT_NODE
(3)
between or inside elements
COMMENT_NODE
(8)
<!-- comments -->
DOCUMENT_NODE
(9)
root node
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;
console.log(doc.nodeType);
console.log(Node.DOCUMENT_NODE);
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>
console.log(document.nodeName);
console.log(elt.nodeName);
console.log(txt.nodeName);
console.log(com.nodeName);
console.log(att.nodeName);
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>
console.log(document.nodeValue);
console.log(div.nodeValue);
console.log(txt.nodeValue);
console.log(com.nodeValue);
console.log(att.nodeValue);
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>
console.log(document.attributes);
console.log(div.attributes.length);
console.log(div.attributes[1].nodeName);
console.log(div.attributes[1].nodeValue);
console.log(txt.attributes);
console.log(com.attributes);
console.log(att.attributes);
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
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;
var body = html.lastChild;
var h1 = body.firstChild;
console.log(h1.nodeName);
h1 = h1.nextSibling;
console.log(h1.nodeName);
console.log(h1.parentNode === body);
console.log(h1.parentNode === html);
var vito = h1.nextSibling.nextSibling;
console.log(vito.childNodes.length);
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;
var children = vito.children;
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]);
var frederico = santino.nextElementSibling;
console.log(frederico === children[1]);
var constanzia = vito.lastElementChild;
console.log(constanzia === children[3]);
var michael = constanzia.previousElementSibling;
console.log(michael === children[2]);
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);
var divs = document.getElementsByTagName('div');
console.log(divs.length);
console.log(divs[3].firstChild.nodeValue);
var sicilians = document.getElementsByClassName('sicilian');
console.log(sicilians[0] === vito);
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);
var vitosChildren = document.querySelectorAll('#vito div');
console.log(vitosChildren.length);
console.log(vitosChildren[2] === michael);
Support for querySelector
and querySelectorAll
is not present on old browers. Look at Can I Use? for more details.
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.id = 'MICHAEL';
console.log(michael.innerHTML);
michael.innerHTML = 'Michael CORLEONE';
console.log(michael.hasAttribute('title'));
michael.setAttribute('title', 'Michael Corleone!!');
console.log(michael.getAttribute('title'));
console.log(michael.className);
michael.classList.add('character');
michael.classList.add('male');
console.log(michael.className);
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';
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);
vito.removeChild(vito.children[2]);
var michael = vito.cloneNode(vito.children[0]);
michael.id = 'michael';
michael.innerHTML = 'Michael Corleone';
vito.insertBefore(michael, vito.children[2]);
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');
console.log(michael1.getAttribute('id'));
console.log(michael2.attr('id'));
michael2.attr('id', 'MICHAEL');
console.log(michael1.innerHTML);
console.log(michael2.html());
michael2.html('Michael CORLEONE');
console.log(michael1 === michael2);
console.log(michael1 === michael2[0]);
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();
var michael = document.createElement('div');
$(michael)
.attr('id', 'michael')
.html('Michael Corleone')
.addClass('character')
.addClass('male')
.insertBefore('#constanzia');
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);
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');