DOM Tips and Techniques: Parent, Child, and Siblings

Louis Lazaris
Share

Complex, markup-heavy web apps are the norm nowadays. A library like jQuery with its ease of use, cross-browser compatibility, and variety of features can help immensely when manipulating HTML on the fly. So it’s no wonder that a lot of developers choose to use such a library rather than native DOM APIs which have been historically quite problematic. Although browser differences are still an issue, the DOM is in much better shape today than it was 5 or 6 years ago when jQuery was really starting to take off.

In this post I’ll discuss and demonstrate a number of different DOM features that can be used to manipulate HTML, with a specific focus on parent, child, and sibling node relationships.

I’ll cover browser support in the conclusion, but keep in mind that a library like jQuery is still generally considered a good option, due to the bugs and inconsistencies in using these kinds of native features.

Counting Child Nodes

Let’s use the following HTML, which I’ll be revisiting in various forms throughout this article:

<body>
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
</body>

If I want to count how many elements are inside the <ul> element, there are two ways I can do that.

var myList = document.getElementById('myList');

console.log(myList.children.length); // 6
console.log(myList.childElementCount); // 6

See the Pen Counting Child Elements: children & childElementCount by SitePoint (@SitePoint) on CodePen.

The logs in the last two lines produce the same result, but each with a different technique. In the first case, I’m using the children property. This read-only property returns a collection of the HTML elements inside the element being queried and I’m using the length property to find out how many are in the collection.

In the second example I’m using childElementCount, which I think is a cleaner and potentially more maintainable way of doing this (in other words, revisiting this code later, you would likely not have any trouble figuring out what childElementCount is doing).

Interestingly, I can also use childNodes.length (instead of children.length), but notice the result:

var myList = document.getElementById('myList');
console.log(myList.childNodes.length); // 13

See the Pen Counting Child Nodes with childNodes.length by SitePoint (@SitePoint) on CodePen.

This returns 13 because childNodes is a collection of all nodes, including whitespace nodes – so keep that in mind if you require the ability to differentiate between all child nodes and all element nodes.

Checking the Existence of Child Nodes

I can also use the hasChildNodes() method to check if a given element has any child nodes. This method returns a Boolean to indicate whether it has child nodes or not:

var myList = document.getElementById('myList');
console.log(myList.hasChildNodes()); // true

See the Pen hasChildNodes() by SitePoint (@SitePoint) on CodePen.

I know my list has child nodes, but I can alter the HTML to remove the elements inside the list so my HTML looks like this:

<body>
  <ul id="myList">
  </ul>  
</body>

Here’s the result if I run hasChildNodes() again:

console.log(myList.hasChildNodes()); // true

See the Pen hasChildNodes() on empty element with white space by SitePoint (@SitePoint) on CodePen.

Notice it still logs true. Although the list doesn’t contain any elements, it does contain whitespace, which is a valid type of node. This particular method deals with nodes in general, not just element nodes. To ensure hasChildNodes() returns false, my HTML would have to look like this:

<body>
  <ul id="myList"></ul>  
</body>

Now I get the expected result from my log:

console.log(myList.hasChildNodes()); // false

See the Pen hasChildNodes() on empty element with no white space by SitePoint (@SitePoint) on CodePen.

Of course, if I know I’m dealing with potential whitespace, I could first check for the existence of child nodes, then determine if any of them are element nodes using the nodeType property.

Adding and Removing Child Elements

There are techniques we can use to add or remove nodes from the DOM. The most familiar, often used in conjunction with createElement(), is the appendChild() method:

var myEl = document.createElement('div');
document.body.appendChild(myEl);

In this case I’m creating a <div> using createElement(), then I’m appending it to the body. Pretty straightforward, and it’s likely you’ve used that technique before.

But instead of inserting a newly-created element, I could also use appendChild() as a simple means of moving an existing element. Let’s say I have the following HTML:

<div id="c">
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
  <p>Example text</p>
</div>

I can change the location of the list using the following code:

var myList = document.getElementById('myList'),
    container = document.getElementById('c');

container.appendChild(myList);

The resulting DOM will look like this:

<div id="c">
  <p>Example text</p>
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
</div>

See the Pen Using appendChild() to change an element’s location by SitePoint (@SitePoint) on CodePen.

Notice that the entire list was removed from its spot (above the paragraph) and then appended to the end of the body, placing it below the paragraph. So although you’ll commonly see appendChild() used to add elements produced using the createElement() method, it’s also an easy way to move an existing element to another location on the page.

I can also remove a child completely from the DOM using removeChild(). Here’s how I can remove the same list element from the previous HTML snippet:

var myList = document.getElementById('myList'),
    container = document.getElementById('c');

container.removeChild(myList);

See the Pen Using removeChild() by SitePoint (@SitePoint) on CodePen.

Now the element is removed. The removeChild() method returns the removed element, so I can store it somewhere if I need to do so for later use:

var myOldChild = document.body.removeChild(myList);
document.body.appendChild(myOldChild);

See the Pen Using removeChild() to move an element by SitePoint (@SitePoint) on CodePen.

There’s also the ChildNode.remove() method, which is a newer feature in the spec:

var myList = document.getElementById('myList');
myList.remove();

See the Pen Using childNode.remove() by SitePoint (@SitePoint) on CodePen.

This method doesn’t return the removed object and doesn’t work in Internet Explorer (only Edge). I should also note that both of these removal techniques will target text nodes as well as elements.

Replacing Child Elements

I can replace an existing child with a new child, whether that new child exists somewhere already, or I’ve created it from scratch. Here’s the HTML:

<p id="par">Example Text</p>

And then the JavaScript:

var myPar = document.getElementById('par'),
    myDiv = document.createElement('div');

myDiv.className = 'example';
myDiv.appendChild(document.createTextNode('New element text'));
document.body.replaceChild(myDiv, myPar);

See the Pen Using replaceChild() by SitePoint (@SitePoint) on CodePen.

Here I’m creating the element, appending a text node with createTextNode(), then inserting it into the page in place of the paragraph element. The previous paragraph element is then replaced with the following HTML:

<div class="example">New element text</div>

As you can see, the replaceChild() method takes two arguments: The new element and the old element that’s being replaced.

I can also use this method to move an existing element from its place and put it in place of a new one. Look at the following HTML:

<p id="par1">Example text 1</p>
<p id="par2">Example text 2</p>
<p id="par3">Example text 3</p>

I can replace the third paragraph with the first paragraph using the following code:

var myPar1 = document.getElementById('par1'),
    myPar3 = document.getElementById('par3');

document.body.replaceChild(myPar1, myPar3);

Now the generated DOM will look like this:

<p id="par2">Example text 2</p>
<p id="par1">Example text 1</p>

See the Pen Using replaceChild() to swap one element for another by SitePoint (@SitePoint) on CodePen.

Targeting Specific Child Elements

There are a few different ways to target specific elements. As seen earlier, I can use the children collection or the childNodes property, and then work from there. But let’s see a few other options.

The firstElementChild and lastElementChild properties do exactly what their names’ suggest: they target the first and last child elements for a specified parent. Let’s go back to our HTML list:

<body>
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
</body>

I can target the first or last elements in this list with the following:

var myList = document.getElementById('myList');
console.log(myList.firstElementChild.innerHTML); // "Example one"
console.log(myList.lastElementChild.innerHTML); // "Example six"

See the Pen Using firstElementChild and lastElementChild by SitePoint (@SitePoint) on CodePen.

I can also use the previousElementSibling and nextElementSibling properties if I want to target child elements other than the first or last. These can be used in conjunction with firstElementChild and lastElementChild:

var myList = document.getElementById('myList');
console.log(myList.firstElementChild.nextElementSibling.innerHTML); // "Example two"
console.log(myList.lastElementChild.previousElementSibling.innerHTML); // "Example five"

See the Pen Using nextElementSibling and previousElementSibling by SitePoint (@SitePoint) on CodePen.

There are also the similar firstChild, lastChild, previousSibling, and nextSibling properties, but those properties deal with all types of nodes, not just element nodes. In many cases element-specific properties are going to be more useful because they only target element nodes.

Injecting Content into the DOM

I’ve already looked at ways to add elements to the DOM. Let’s address a similar concept, and look at a few features that give you more fine-grained control over where you insert content.

First, there’s the straightforward insertBefore() method. This works much like replaceChild(), taking two arguments and it will work with a newly-created element or an existing one. Let’s use the following HTML:

<div id="c">
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
  <p id="par">Example Paragraph</p>
</div>

Notice the paragraph element. I’m going to remove the paragraph, then insert it before the list, all in one fell swoop:

var myList = document.getElementById('myList'),
    container = document.getElementBy('c'),
    myPar = document.getElementById('par');

container.insertBefore(myPar, myList);

See the Pen Using insertBefore() by SitePoint (@SitePoint) on CodePen.

The resulting HTML will have the paragraph element before the list, instead of after, which is yet another way to move an element from its existing spot to another place in the page:

<div id="c">
  <p id="par">Example Paragraph</p>
  <ul id="myList">
    <li>Example one</li>
    <li>Example two</li>
    <li>Example three</li>
    <li>Example four</li>
    <li>Example five</li>
    <li>Example Six</li>
  </ul>
</div>

As with replaceChild(), insertBefore() takes two arguments: the element that’s being added and the element before which we want to add the element specified in the first argument.

That method is fairly straightforward. Let’s now use a much more powerful method to get more precise with these insertions: the insertAdjacentHTML() method.

var myEl = document.getElementById('el');
myEl.insertAdjacentHTML('beforebegin', '<p>New element</p>');

The above code will insert the specified string of content before the targeted element (in this case, before the list). The second argument is the string of HTML that you want to insert (this could also just be text with no HTML tags). The first argument, also represented as a string, has to be one of the following four values:

  • ‘beforebegin’ – Inserts the string before the specified element
  • ‘afterbegin’ – Inserts the string inside the specified element, before its first child
  • ‘beforeend’ – Inserts the string inside the specified element, after its last child
  • ‘afterend’ – Inserts the string after the specified element

To make it easier to understand what each of these values represents, look at the HTML comments in the following snippet. Assuming the #el div is the targeted element, each comment indicates where the HTML would go with the specified value for the first argument:

<!-- beforebegin -->
<div id="el">
  <!-- afterbegin -->
  <p>Some example text</p>
  <!-- beforeend -->
</div>
<!-- afterend -->

That should make it fairly clear what each of the possible values do with the string of content.

See the Pen Using insertAdjacentHTML() by SitePoint (@SitePoint) on CodePen.

Browser Support

When discussing anything in the DOM, browser support is always a big factor. Bugs aside, below is a rundown of how well these features are supported.

The following features are supported in all browsers, including old versions of IE:

The following features are supported in IE9+ and all other browsers:

That leaves only the Node.remove() method, which, as mentioned, has no support in Internet Explorer, but has been added to Microsoft’s new Edge browser.

So overall, browser support for these DOM features is excellent. Of course, there will be bugs and interoperability issues, so make sure you do thorough testing if you decide to make heavy use of any of these.

Conclusion

Ideally, the JavaScript language and the native DOM APIs should be enough to allow us to build stuff easily and in a cross-browser manner. Eventually I think we will get to that point, but in the meantime, tools like jQuery will continue to fill this need.

Feel free to comment on any related features that you’ve discovered or used when attempting to manipulate HTML on the fly with native DOM scripting.