MaiDeveloper

How to Traverse DOM Elements

There is a traversal API that allows us to find structurally related elements of the document; it treats a document as a tree of element objects. We can access these traversal API by referring to a specific element's parent, children, and siblings properties. This traversal API ignores the text nodes; although they are part of the document.

Let's get started with these properties:

parentNode { Node }

The parent of the element.

children { HTMLCollection }

The HTML collection contains the element children of the elemen. It includes only element nodes; excludes non-element children like text nodes and comment nodes.

childElementCount { number }

The number of element children; same as children.length.

firstElementChild { Node }

Refer to the first element child.

lastElementChild { Node }

Refer to the last element child.

nextElementSibling { Node }

Refer to the sibling element immediately after the element.

previousElementSibling {Node }

Refer to the sibling element immediately before the element.

Usage

document.children[0]
document.firstElementChild.nextElementSibling
document.lastElementChild.parentNode
document.childElementCount

Ways of Traversal

There are two ways to traverse every element in the document. One is recursively doing a depth-first traversal of a document. Another one is the breath first search using the queue data structure. They both have a time complexity of O(N) and space complexity of O(N).

body span div img a nav

Depth First Search (DFS)


/**
 * @callback callbackFunc
 * @property {Node}
 */

/**
 * @param {Node} element
 * @param {callbackFunc} callback
 */
const dfs = (element, callback) => {
  // Do something with the element
  callback(element);

  // Traverse the children
  for (const child of element.children) {
    dfs(child, callback);
  }
};

In the example DOM, this would run in preorder depth first search: body, div, a, span, nav, img.

Breath First Search (BFS)


const bfs = (element, callback) => {
  const queue = [element];

  while (queue.length) {
    // Get the next element
    const node = queue.shift();

    // Do something with the element
    callback(node);

    // Find the element's children
    for (const child of node.children) {
      // Add the child to the queue
      queue.push(child);
    }
  }
};

In the example DOM, this would run in breath first search: body, div, span, img, a, nav.

Different ways to traverse an element's children


for (const child of element.children) {
  // Do something with the child
}


const child = element.firstElementChild;

while (child !== null) {
  // Do something with the child
  
  child = child.nextElementSibling;
}

Documents As Trees of Nodes

There is a separate set of properties defined on all Node objects that you can utilize when you don't want to ignore the Text nodes and Comment nodes while traversing a document or element.

Let's get started with these properties:

parentNode { Node }

The parent of the node.

childNodes { NodeList }

A NodeList contains all the children (not just element children).

firstChild { Node }

The first child node of the node.

lastChild { Node }

The last child node of the node.

nextSibling { Node }

The next sibling node of the node.

previousSibling { Node }

The previous sibling node of the node.

nodeValue { string }

The text content of a text or comment node.

nodeName { string }

The uppercase html tag name of the element.

NodeType { number }

A number refer to a specific kind of node.

Node.ELEMENT_NODE   ( 1 )

An element node like p or div.

Node.ATTRIBUTE_NODE   ( 2 )

An attribute of an element.

Node.TEXT_NODE   ( 3 )

The text inside an element.

Node.CDATA_SECTION_NODE   ( 4 )

A CDATASection like <[!CDATA[ ... ]]>

Node.PROCESSING_INSTRUCTION_NODE   ( 7 )

An element node like p or div.

Node.COMMENT_NODE   ( 8 )

A comment node like <!-- ... -->

Node.COMMENT_NODE   ( 9 )

A document node.

Node.DOCUMENT_TYPE_NODE   ( 10 )

A document type node, like <!DOCTYPE html>.

Node.DOCUMENT_FRAGMENT_NODE   ( 11 )

A document fragment node.

Example


/**
 * Get text content of a element recursively
 * @param {Node} element
 * @returns {string}
 */
const getText = element => {
  let text = '';

  // Traverse each child
  for (const node of element.childNodes) {
    // Check the node type
    switch (node.nodeType) {
      case Node.TEXT_NODE:  // 3
        text += node.nodeValue;
        break;
      case Node.ELEMENT_NODE: // 1
        text += getText(element);
    }
  }

  return text;
};

There is another way of traversing the child nodes.


let child = element.firstChild;

while (child !== null) {
  // Do something with child

  child = child.nextSibling;
}



Mike Mai
Mike Mai   Brooklyn, New York
I am full-stack web developer, passionate about building world class web applications. Knowledge in designing, coding, testing, and debugging. I love to solve problems.