HTML & CSS
Article

DOM Tips and Techniques: Parent, Child, and Siblings

By Louis Lazaris

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.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • PVgr

    Essential read for beginners! Thanks Louis!

  • http://www.adriansandu.com Adrian SANDU

    First I must admit I find the title and the category of this article a bit misleading. I was expecting something more related to HTML only, maybe some new stuff from the W3C. Instead the article is about native Javascript support for DOM navigation. I just feel that it would have belonged better to the Javascript category.

    Now, don’t take me wrong, the subject itself is great and it’s wonderful that the native support has increased to this level. Armed with this sort of support we can now get rid of jQuery as a dependency in some cases while keeping the code faster, leaner and easier to maintain.

    • LouisLazaris

      Well, the title does say “DOM tips…” And technically, the DOM is not really JavaScript, it just so happens that JavaScript is the only language we use to do DOM scripting.

      But anyhow, I actually discussed this with the JS editors Jim and Aurelio, and I’m going to occasionally post DOM-related articles that focus more on manipulating HTML and CSS.

      And FWIW, this is considered part of the “HTML5” spec, which falls under the HTML/CSS category on SitePoint, and I’ve cross-posted it to JavaScript.

      In other words, I have a million excuses for not putting this through the JS editors. :)

  • Antonella

    Fantastic reference article, thanks! I’m a heavy jQuery user but will need to start thinking about getting away from it eventually.

  • LouisLazaris

    Good question. When you call insertBefore(), the node it’s called on needs to be the parent of the element before which you’re inserting the new element. But the inserted element, if I’m understanding correctly, can be from anywhere.

    Here’s an example where the inserted element is outside the container:

    https://jsbin.com/ruyuzu/edit?html,js,output

  • http://studiorgb.uk Paweł Grzybek

    Fantastic article! I found it very useful and I bookmark it as a reference. Thanks a ton Louis.
    Just one small thing: `el.insertAdjacentHTML` i belive should be `myEl.insertAdjacentHTML`

    Thanks again!

    • LouisLazaris

      Yes, of course! Thank you, I’ve corrected it. I think it was just a copy/paste error.

  • http://www.themarketingtechnologist.com Siebe Hiemstra

    Nice one!

  • Glenn Allen

    Great article Louis!
    I’ve also found that the “querySelector” and “querySelectorAll” methods are also relatively unknown by developers who always use libraries like JQuery – could potentially be added to this or a future article

    • LouisLazaris

      Yes, probably true for jQuery devs or just people who had to support oldIE for so long. But I do find those are much more well known than most of the ones I mention here.

      As a sidepoint, IE8 actually does support those, but only when used with selectors that it supports (which is fine, because I doubt anyone is using weird selectors with those methods).

  • http://webdevrn.com/ Chris Stephens

    Fun read! I like jQuery and even though it is not a “necessity” it is still a good library. I really wish the native classList API was supported in IE9. Everything else I could live without using graceful degradation. Know of any other IE9 class techniques?

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.