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 DocumentFragment
s 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
}
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);
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);
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 DocumentFragment
s from strings with Range
s
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>
`;
This is nice if you’re trying to recreate JSX, but it’s not as performant as using DocumentFragment
s. 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);
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!