
Photo by Alex Radelich on Unsplash

The topmost tree nodes are available directly as document properties:
<html> = document.documentElementdocument.documentElement. That’s the DOM node of the <html> tag<body> = document.bodydocument.body.<head> = document.headdocument.head.<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>script test</title>
<script>
alert("From HEAD: " + document.body );
// From HEAD: null
</script>
</head>
<body>
<p>
Tony Test
</p>
<script>
alert("From BODY: "+ document.body);
// From BODY: [object HTMLBodyElement]
</script>
</body>
</html>
document.body.childNodes.lengthdocument.body.childNodes[i]Properties firstChild and lastChild give fasr access to the first and last children.
elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
There’s also a special function elem.hasChildNodes()
As we can see, childNodes looks like an array. But actually it’s not an array, but rather a collection - a special array-like iterable object.
There are two important consequences:
for..of to iterate over it:for (let node of document.body.childNodes) {
alert(node); // shows all nodes from the collection
}
that’s because it’s iterable (provides the Symbol.iterator property, as required).
alert(document.body.childNodes.filter);
// undefined (there's no filter method)
The first thing is nice. The second is tolerable, because we can use Array.from to create a “real” array from the collection, if we want array method:
alert( Array.from(document.body.childNodes).filter ); // function
DOM collections, and even more - all navigation properties listed in this chapter are read-only.
We can’t replace a child by something else by assigning childNodes[i] = ....
Changing DOM needs other methods. We will see them in the next chapter.
DOM collections are live
Almost all DOM collections with minor exceptions are live. In other words, they refelct the current state of DOM.
If we keep a reference to elem.childNodes, and add/remove nodes into DOM, then they appear in the collection automatically.
Don’t use for..in to loop over collections
Collections are iterable using for..of. Sometimes people try to use for..in for that.
Please. don’t. The for..in loop iterates over all enumerable properties. And collections have some “extra” rarely used properties that we usually do not want to get:
<body>
<script>
// show 0, 1, length, item, values and more.
for (let prop in document.body.childNodes) alert(prop);
</script>
</body>
nextSibling property, and the previous one - in previousSibling.// parent of <body> is <html>
alert( document.body.parentNode === document.documentElement ); //true\
// after <head> goes <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// before <body> goes <head>
alert( document.body.previousSibling ); // HTMLHeadElement

document.documentElementdocument.bodyparentElementpreviouselementSiblingnextElementSiblingfirstElementChildchildrenlastElementChildWhy parentElement? Can the parent be not an element?
The parentElement property returns the “element” parent, while parentNode return “any node” parent. These properties are usually the same: they both get the parent.
With the one exception of document.documentElement:
alert( document.documentElement.parentNode ); // document
alert( document.documentElement.parentElement ); // null
The reason is that the root node document.documentElement(<html>) has document as its parent. But document is not an element node, so parentNode returns it and parentElement does not.
This detail may be useful when we want to travel up from an arbitrary element elem to , but not to the document:
while(elem = elem.parentElement) { // go up till <html>
alert( elem )
}
Let’s modify one of the examples above: replace childNodeswith children. Now it shows only element:
<html>
<head></head>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
<ul>
<div>End</div>
<script>
for(let elme of document.body.children) {
alert(elem); // DIV, UL, DIV, SCRIPT
}
</script>
</body>
</html>
The <table> element supports (in addition to the given above) these properties:
table.rows - the collection of <tr> elements of the table.table.caption/tHead/thead/tFoot - references to elements <caption>, <thead>, <tfoot>.table.tBodies - the collection of <tbody> element(can be many according to the standard, but there will always be at least one - even if it is not in the source HTML, the browser will put it in the DOM).<thead>, <tfoot>, <tbody> elements provide the row property:
tbody.rows - the collection of <tr> inside.<tr>:
tr.cells - the collection of and cells inside the given .tr.sectionRowIndex - the position (index) of the given inside the enclosing <thead>/<tbody>/<tfoot>.tr.rowIndex - the number of the in the table as a whole (including all table rows).An example of usage:
<table id = "table">
<tr>
</tr>
<td>one</td><td>two</td>
<tr>
<td>three</td><td>four</td>
</tr>
</table>
<script>
// get td with "two" (first row, second column)
let td = table.rows[0].cells[1];
td.style.backgroundColor = "red"; // highlight it
</script>
Given a DOM node, we can go to its immediate neighbors using navigation properties.
There are two main sets of them:
parentNode, chilNodes, firstChild, lastChild, previousSibling, nextSibling.parentElement, children, firstElementChild, lastElementChild, previousElementSibling, nextElementSibling.<html>
<body>
<div>User:</div>
<ul>
<li>John</li>
<li>Pete</li>
</ul>
</body>
<script>
// The <div> DOM node?
alert(document.body.firstElementChild);
alert(document.body.children[0]);
alert(document.body.childNodes[1]);
// The <ul> DOM node?
alert(document.body.lastElementChild);
alert(document.body.children[1])
// The second <li> (with Pete)?
alert(document.body.lastelementChild.lastElementChild);
</script>
</html>
firstElementChild / lastElementChildIf elem - is an arbitrary DOM element node…
elem.lastChild.nextSibling is always null?elem.lastChildis always the last one, it has no nextSilbling.elem.children[0].previousSibling is always null?elem.children[0] is the first child among elements. But there may exist non-element nodes before it. So previousSibling may be a text node.Please note: for both cases if there are no children, then there will be an error.
If there are no children, elem.lastChild is null, so we can’t access elem.lastchild.nextSibling. And the collection elem.children is empty (like an empty array [])
Write the code to paint all diagonal table cells in red.
You’ll need to get all diagonal <td> from the <table> and paint them using the code:
// td should be the reference to the table cell
td.style backgroundColor = 'red';
The result should be:

// script
let table = document.body.firstElementChild;
for (let i = 0; i < table.rows.length; i++) {
let row = table.rows[i];
row.cells[i].style.backgroundColor = 'red';
}