DEV Community

Ryan Boone
Ryan Boone

Posted on • Edited on • Originally published at falldowngoboone.com

Tips for vanilla JavaScript DOM manipulation

If you need to go au naturale with your JavaScript DOM manipulation, here are some tips for improving performance.

Use DocumentFragments to add multiple elements

Here's one way you might add multiple DOM nodes to a mounted node:

var root = document.getElementById('fruit-list');
var fruitItems = ['apple', 'orange', 'banana'].map(function(fruit) {
    var item = document.createElement('li');
    item.innerText = fruit;
    return item;
});

for (var i = 0; i < fruitItems.length; i++) {
    root.appendChild(fruitItems[i]); // page reflows every time
}
Enter fullscreen mode Exit fullscreen mode

This code works, but the issue is the page will reflow every time appendChild is called. If you have a long list of items to add, you're going to end up in a serious performance bottleneck, and an unhappy boss. The solution is to use a [DocumentFragment](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment):

var root = document.getElementById('fruit-list');
var fragment = document.crateDocumentFragment();
var fruitItems = ['apple', 'orange', 'banana'].map(function(fruit) {
    var item = document.createElement('li');
    item.innerText = fruit;
    return item;
});

for (var i = 0; i < fruitItems.length; i++) {
    fragment.appendChild(fruitItems[i]); // no page reflow!
}

root.appendChild(fragment);
Enter fullscreen mode Exit fullscreen mode

The appendChild method is only called once, and this makes browsers (and my boss) very happy.

But if you can, use ParentNode.append

You can think of the [ParentNode.append](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append) method as appendChild on steroids (sans the rage and adult acne). Unlike its puny cousin appendChild, append can take multiple nodes, automatically converts string arguments to text nodes, and it utilizes DocumentFragment for us:

// the `consts` are my way of letting you know this is newer...šŸ™ƒ

const root = document.getElementById('fruit-list');
const fragment = document.crateDocumentFragment();
const fruitItems = ['apple', 'orange', 'banana'].map(function(fruit) {
    const item = document.createElement('li');
    item.innerText = fruit;
    return item;
});

root.append(...fruitItems);
Enter fullscreen mode Exit fullscreen mode

This is the most convenient way to add multiple nodes to a parent node. Support is great if you don't have to prop up Internet Explorer. Luckily, if you do, there's a polyfill for that.

Create DocumentFragments from strings with Ranges

Imagine a world where you want to create HTML from a string. You might do something like this:

// orange you getting tired of this example yet?

const root = document.getElementById('fruit-list');

root.innerHTML = `
    <li>apple</li>
    <li>orange</li>
    <li>banana</li>
`;
Enter fullscreen mode Exit fullscreen mode

This is nice if you're trying to recreate JSX, but it's not as performant as using DocumentFragments. Luckily there's a way to directly create a DocumentFragment from a string. Contrived code warning:

const root = document.getElementById('fruit-list');
const fragment = document.createRange().createContextualFragment(`
    <li>apple</li>
    <li>orange</li>
    <li>banana</li>
`);

root.appendChild(fragment);
Enter fullscreen mode Exit fullscreen mode

The createRange method returns a Range, which is a representation of a sliver of the current DOM document. The createContextualFragment creates a DocumentFragment using a parsing algorithm based on the current document's context (in this case, HTML). Range methods are meant to be convenience methods built on top of common node editing patterns with optimization in mind, and I'm quite interested to learn more about them.

Memorize the DOM properties that trigger layout

The DOM API is tricky because just observing certain node properties can trigger page layout. Doing this multiple times in a row can be a performance problem. Doing this inside a loop can cause layout thrashing (trust me, it's as bad as it sounds).

You'll want to be aware of what DOM properties cause the browser to trigger layout, so you need to get to memorizing. Or you could simply bookmark this convenient list of properties that cause layout.

This is only scratching the proverbial surface

There's more to DOM layout with vanilla JavaScript, to be sure. I'm interested in looking at some of the performance optimizations VDOM libraries employ to eek the most out of DOM manipulation. I kinda like that sort of thing.

I hope you learned something new today. And if you did, please consider liking this post on DEV Community, and let me know on Twitter. I get lonely sometimes.

Until next time!

Top comments (0)