Useful DOM Traversal Methods

John Au-Yeung - Jan 23 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

The main use of client-side JavaScript is to manipulate web pages dynamically. We can do this with the DOM traversal methods and properties available to DOM Node objects.

Adding or changing child and sibling elements is easy for any given Node since there are properties built into DOM Node objects to perform these actions. The following are methods of a DOM Node object for getting parent, child, and sibling nodes or elements.

appendChild

The appendChild methods let us attach a child node to a given HTML element as the last child node of the current node. If the argument referenced an existing node on the DOM tree then the node will be detached from its current position and attached to its new position.

It takes one argument, which is a DOM Node object.

For example, given 2 existing nodes in the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>
Enter fullscreen mode Exit fullscreen mode

We can attach the element with ID bar as a child to the element with ID bar as follows:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
foo.appendChild(bar);
Enter fullscreen mode Exit fullscreen mode

Once we ran that, we should get the following structure:

<div id="foo">  
  foo  
  <div id="bar">  
    bar  
  </div>  
</div>
Enter fullscreen mode Exit fullscreen mode

We can also use it to create an element that’s created on the fly. For example, if we have the following HTML:

<div id='foo'>  
  foo  
</div>
Enter fullscreen mode Exit fullscreen mode

Then we can write the following code to attach a new p element to the div with ID foo:

const foo = document.querySelector('#foo');  
const bar = document.createElement('p');  
bar.textContent = 'bar';
foo.appendChild(bar);
Enter fullscreen mode Exit fullscreen mode

In the code above, we used createElement to create a new p element. Then we set the textContent property to add text inside the p element. At the end, we can appendChild on foo with bar as the argument to attach bar as the child of foo .

cloneNode

The cloneNode method clones a Node object and optionally, all of its content. It doesn’t clone all the content of the Node by default.

It takes one argument, which is an optional argument indicate if it’s a deep clone, which means everything will be cloned. true means do a deep clone and false otherwise.

For example, given the following HTML:

<div>  
  foo  
</div>
Enter fullscreen mode Exit fullscreen mode

We can write the following JavaScript code to clone the div and then attach it to the body element as the last child:

const fooDiv = document.querySelector('div');  
const fooClone = fooDiv.cloneNode(true);  
document.body.appendChild(fooClone);
Enter fullscreen mode Exit fullscreen mode

We pass in true to the cloneNode method to clone everything. Then we call appendChild on document.body with the cloned object passed in as the argument to add it as the child of body.

compareDocumentPosition

The compareDocumentPosition method compares the position of the given node against another node in any document. It takes a DOM Node object as its argument.

It returns a bitmask with the following possible values

  • DOCUMENT_POSITION_DISCONNECTED — 1
  • DOCUMENT_POSITION_PRECEDING — 2
  • DOCUMENT_POSITION_FOLLOWING — 4
  • DOCUMENT_POSITION_CONTAINS — 8
  • DOCUMENT_POSITION_CONTAINED_BY — 16
  • DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC — 32

For example, given the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>
Enter fullscreen mode Exit fullscreen mode

We can write the following JavaScript to compare the position of the div with ID foo and the one with ID bar:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
const relativePos = foo.compareDocumentPosition(bar);  
console.log(relativePos);
Enter fullscreen mode Exit fullscreen mode

The code above should get us 4 for relativePos, which means the element with ID bar follows the element with ID foo.

contains

The contains method checks if a DOM node is inside the given node. It takes one argument, which is a Node object that we want to check if it’s inside the one that this method is called on.

It returns true if the node in the argument is inside the one that’s called and false otherwise.

For example, given the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>
Enter fullscreen mode Exit fullscreen mode

Then we can write the following JavaScript to check if the div with ID bar is inside the div with ID foo:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
const fooContainsBar = foo.contains(bar);  
console.log(fooContainsBar);
Enter fullscreen mode Exit fullscreen mode

Of course, fooContainsBar should be false since the div with ID foo isn’t inside the div with ID bar.

On the other hand, if we have the following HTML instead:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Then with the same JavaScript code as the first example, fooContainsBar should true since div with ID foo is inside the div with ID bar.

getRootNode

The getRootNode method returns the Node object’s root, which optionally includes the shadow root if it’s available.

It takes an optional argument with an object with the following properties:

  • composed — a boolean that indicates rather the shadow root should be included. It defaults to false

It returns the Node that either returns an element that’s the root of the given Node or the shadow root will be returned for elements inside the shadow DOM.

For example, if we have the following HTML:

<div id='foo'>  
  foo  
</div>
Enter fullscreen mode Exit fullscreen mode

Then we can call the getRootNode method as follows:

const foo = document.querySelector('#foo');const root = foo.getRootNode();  
console.log(root);
Enter fullscreen mode Exit fullscreen mode

We should get the HTML document as the root node since it’s not inside a shadow DOM.

The root will be the shadow root for Web Components. For example, if we have the following web component:

customElements.define('custom-p',  
  class extends HTMLElement {  
    constructor() {  
      super(); const pElem = document.createElement('p');  
      pElem.id = 'p-element';  
      pElem.textContent = 'shadow p' 
      const shadowRoot = this.attachShadow({  
        mode: 'open'  
      });  
      shadowRoot.appendChild(pElem);  
    }  
  }  
);
Enter fullscreen mode Exit fullscreen mode

And we have the Web Component added in the HTML code:

<custom-p></custom-p>
Enter fullscreen mode Exit fullscreen mode

Then we can get the root node of the element with the ID p-element by writing:

const pElement = document.querySelector('custom-p').shadowRoot.querySelector('#p-element');  
const rootNode = pElement.getRootNode();  
console.log(rootNode);
Enter fullscreen mode Exit fullscreen mode

First, we get the custom element, then we get the shadowRoot property which lets us access the shadow DOM of our custom-p web component. Then we can get the p element with the ID p-element with the shadow root.

After that, we get the root node of it by callinggetRootNode on pElement which represents p element with the ID p-element .

The console.log should get us the shadow root of the Web Component.

Conclusion

With these DOM traversal methods, we can set the nodes however we like for simple situations. Also, there’re methods to check whether an element is the child of another one, and also to get the root node of the given element.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .