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>
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);
Once we ran that, we should get the following structure:
<div id="foo">
foo
<div id="bar">
bar
</div>
</div>
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>
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);
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>
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);
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
— 1DOCUMENT_POSITION_PRECEDING
— 2DOCUMENT_POSITION_FOLLOWING
— 4DOCUMENT_POSITION_CONTAINS
— 8DOCUMENT_POSITION_CONTAINED_BY
— 16DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
— 32
For example, given the following HTML:
<div id='foo'>
foo
</div>
<div id='bar'>
bar
</div>
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);
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>
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);
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>
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 tofalse
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>
Then we can call the getRootNode
method as follows:
const foo = document.querySelector('#foo');const root = foo.getRootNode();
console.log(root);
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);
}
}
);
And we have the Web Component added in the HTML code:
<custom-p></custom-p>
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);
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.