The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object. All operations on the DOM start with the document
object. That’s the main “entry point” to DOM. From it we can access any node. DOM tree refers to the HTML page where all the nodes are objects. There can be 3 main types of nodes in the DOM tree:
- text nodes
- element nodes
- comment nodes
According to the W3C HTML DOM standard, everything in an HTML document is a node:
- The entire document is a document node
- Every HTML element is an element node
- The text inside HTML elements are text nodes
- Every HTML attribute is an attribute node (deprecated)
- All comments are comment nodes
With the HTML DOM, all nodes in the node tree can be accessed by JavaScript. New nodes can be created, and all nodes can be modified or deleted.
The topmost tree nodes are available directly as document properties:
<html>
= document.documentElement
The topmost document node is document.documentElement
. That’s the DOM node of the <html>
tag.
<body>
= document.body
Another widely used DOM node is the <body>
element – document.body
.
<head>
= document.head
The <head>
tag is available as document.head
.
Note: A text node is always a leaf of the tree.
Note: In the DOM world null
means “doesn’t exist” or “no such node”.
<html>
<head>
<title>hello</title>
</head>
<body>
...
</body>
</html>
<script>
console.log(document.title); // hello
console.log(document.documentElement); // html tag
console.log(document.head); // head tag
console.log(document.body); // body tag
console.log(document); // complete html file
</script>
Auto correction
If an erroneous HTML is encountered by the browser it tends to correct it. For example, if we put something after the body, it is automatically moved inside the body. Another example is <table>
tag which must contain <tbody>
.
Note: document.body
can sometimes be null
if the JavaScript is written before the <body>
tag.
Children of an element
Direct as well as deeply nested elements of an element are called its children.
Child nodes => Elements that are direct children. For example, <head>
and <body>
tag are children of <html>
.
Descendant nodes => All nested elements, children, their children and so on.
firstChild, lastChild and childNodes
element.firstChild
=> first child element
element.lastChild
=> last child element
element.childNodes
=> all child element
Following is always true:
element.childNodes[0]
=== element.firstChild
element.childNodes[element.childNodes.length-1]
=== element.lastChild
There is also a method element.hasChildNodes()
to check whether there are any child nodes.
Note: childNodes
looks like an array. But its not actually an array but a collection. We can use Array.from(collection)
to convert it into an Array.
<body><div>
<p>this is a paragraph</p>
<span>Span</span>
</div>
</body>
<script>
console.log(document.body.firstChild); // div tag and inside div contents
console.log(document.body.lastChild); // script tag and inside script contents
console.log(document.body.childNodes);
let arr = Array.from(document.body.childNodes);
console.log(arr);
// (3) [div, text, script]
// 0: div
// 1: text
// 2: script
// length: 3
// [[Prototype]]: Array(0)
</script>
element.childNodes
variable (reference) will automatically update if childNodes of element is changed.Siblings and the parent
Siblings are nodes that are children of the same parent. For example, <head>
and <body>
are siblings. Siblings have same parent. In the above example its <html>
.
<body>
is said to be the next
or right
sibling of <head>
.
<head>
is said to be the previous
or left
sibling of <body>
.
The next sibling is in nextSibling
property and the previous one in previousSibling
. The parent is available as parentNode
.
<body><div><div> class="first">first</div><div> class="second">second</div></div></body>
<script>
console.log(document.body.firstChild); // div tag
let a = document.body.firstChild;
console.log(a.parentNode); // body tag
console.log(a.parentElement); // body tag
console.log(a.firstChild.nextSibling); // div.second
console.log(document.documentElement.parentNode); // document
console.log(document.documentElement.parentElement); // null
</script>
Element only Navigation
Sometimes we don't want text or comment nodes. Some links only take element nodes into account. For example,
document.previousElementSibling
=> previous sibling which is an element
document.nextElementSibling
=> next sibling (element)
document.firstElementChild
=> first element child
document.lastElementChild
=> last element child
<body><!-- this is a comment -->
<nav>
<ul>
<li>Home</li>
<li>About</li>
<li>Hire Me</li>
</ul>
</nav>
</body>
<script>
const b = document.body;
console.log(b.firstChild); // comment
console.log(b.firstElementChild); // nav tag
</script>
Table links
certain DOM elements may provide additional properties specific to their type for convenience. Table element supports the following properties:
table.rows
=> collection of <tr>
elements
table.caption
=> reference to <caption>
table.tHead
=> reference to <thead>
table.tFoot
=> reference to <tfoot>
table.tBodies
=> collection of <tbody>
elements
tbody.rows
=> collection of <tr>
inside
tr.cells
=> collection of <td>
and <th>
tr.sectionRowIndex
=> index of <tr>
inside enclosing element
tr.rowIndex
=> row number starting from 0
td.cellIndex
=> number of cells inside enclosing <tr>
<body>
<div class="container my-4">
<table class="table">
<thead>
<tr>
<th> scope="col">#</th>
<th> scope="col">First</th>
<th> scope="col">Last</th>
<th> scope="col">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th> scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<t>>@mdo</t>
</tr>
<tr>
<th> scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<th> scope="row">3</th>
<td> colspan="2">Larry the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
const t = document.body.firstElementChild.firstElementChild;
console.log(t); // table tag
console.log(t.rows); // HTMLCollection(4)0: tr1: tr2: tr3: trlength: 4[[Prototype]]: HTMLCollection
console.log(t.caption); // null
console.log(t.thead); // undefined
console.log(t.tHead.firstElementChild); // tr tag
console.log(t.tBodies); // HTMLCollection [tbody]0: tbodylength: 1[[Prototype]]: HTMLCollection
console.log(t.tFoot); // null
console.log(t.tbody.rows);
console.log(t.rows[0].rowIndex); // 0
console.log(typeof document); // object
console.log(typeof window); // object
</script>
Searching the DOM
DOM navigation properties are helpful when the elements are close to each other. If they are not close to each other, we have some more methods to search the DOM.
document.getElementById
- this method is used to get the element with a given
id
attribute. document.querySelectorAll
- returns all elements inside an element matching the given CSS selector.
document.querySelector
- returns the first element for the given CSS selector. A efficient version of
element.querySelectorAll(CSS)[0]
document.getElementsByTagName
- returns elements with the given tag name
document.getElementsByClassName
- returns elements that have the given CSS class. Don't forget the
s
letter document.getElementsByName
- searches elements by the name attribute
let span = document.getElementById('span');
span.style.color = 'orange';
<script>
// change card title to orange
let cartTitle = document.getElementsByClassName('card-title')[0];
cartTitle.style.color = "orange";
let cardText = document.getElementById('cardText');
cardText.style.color = "blue";
let query = document.querySelectorAll('.card-title');
console.log(query);
query[0].style.color = 'orange';
query[1].style.color = 'orangered';
query[2].style.color = 'red';
document.querySelector('.button').style.color = 'brown';
document.querySelector('.button').style.fontFamily = 'cursive';
console.log(document.getElementsByTagName('a'));
console.log(document.querySelector('.card-title').getElementsByTagName('a'));
console.log(document.querySelector('.card').getElementsByTagName('a'));
console.log(document.getElementsByName('search'));
</script>
matches, closest and contains methods
There are three important methods to search the DOM:
elem.matches(CSS)
- to check if element matches the given CSS selector.
elem.closest(CSS)
- to look for the nearest ancestor that matches the given CSS selector. The elem itself is also checked.
elemA.contains(elemB)
- returns true if elemB is inside elemA (a descendant of elemA) or when elemA == elemB
<body>
<div class="box" id="id1">this is an element 1.
<span> id="sp1">this is a span</span>
</div>
<div> class="box" id="id2">this is an element 2.</div>
</body>
<script>
const id = document.getElementById('id1');
console.log(id); // div.box#id1
console.log(id.matches('.class')); // false
console.log(id.matches('.box')); // true
const sp = document.getElementById('sp1');
console.log(sp.closest('.box')); // div.box#id1
console.log(sp.contains(id)); // false
console.log(id.contains(sp)); // true
console.log(sp.contains(sp)); // true
</script>