Walking the DOM for Javascript

sunghoon·2025년 1월 22일
0

2.0 Glove Project

목록 보기
12/40
post-thumbnail

Photo by Alex Radelich on Unsplash

1.3 walking the DOM

Link

On top: documentElement and body

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 element — document.body.
  • <head> = document.head
    • The tag is available as document.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>

Children: childNodes, firstChild, lastChild

  • There are two terms that we’ll use from now on:
    • Child nodes (or children) - elements that are direct children. In other words, they are nested exactly in the given one. For instance, and are children of element.
    • Descendants - all elements that are nested in the given one, including children, their children and so on.
  • document.body.childNodes.length
  • document.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()

DOM collections

link

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:

  1. We can use 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).

  1. Array methods won’t work because it’s not an array:
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 are read-only
    • 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>

Siblings and the parent

  • The next sibling is in 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

Element-only navigation

  • document.documentElement
  • document.body
  • parentElement
  • previouselementSibling
  • nextElementSibling
  • firstElementChild
  • children
  • lastElementChild

Why 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>

Summary

Given a DOM node, we can go to its immediate neighbors using navigation properties.
There are two main sets of them:

  • For all nodes:
    parentNode, chilNodes, firstChild, lastChild, previousSibling, nextSibling.
  • For element nodes only:
    parentElement, children, firstElementChild, lastElementChild, previousElementSibling, nextElementSibling.

Question

1. DOM children

<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 / lastElementChild

The sibling question

If elem - is an arbitrary DOM element node…

  • Is it true that elem.lastChild.nextSibling is always null?
    • Yes, true The element elem.lastChildis always the last one, it has no nextSilbling.
  • Is it true that elem.children[0].previousSibling is always null?
    • No. wrong, because 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 [])

Select all diagonal cells

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';
}
profile
프라다 신은 빈지노와 쿠페를 타는 꿈을 꿨다.

0개의 댓글