I was always under the impression that rather than touching the DOM repeatedly, for performance reasons, we should use a documentFragment
to append multiple element
Well actually your test code just insert nodes and do not alter their content or CSS which would in fact force the rendering engine to a reflow.
I have prepared 3 tests to demonstrate this dramatic difference.
window.requestAnimationFrame()
document.createDocumentFragment()
Notes:
// Resets the divs
function resetLayout() {
divs = document.querySelectorAll('div');
speed.textContent = "Resetting Layout...";
setTimeout(function() {
each.call(divs, function(div) {
div.style.height = '';
div.offsetTop;
});
speed.textContent = "";
}, 16);
}
// print the result
function renderSpeed(ms) {
speed.textContent = ms + 'ms';
}
var divs = document.querySelectorAll('div'),
raf = window.requestAnimationFrame,
each = Array.prototype.forEach,
isAfterVdom = false,
start = 0;
// Reset the Layout
reset.onclick = resetLayout;
// Direct DOM Access
direct.onclick = function() {
isAfterVdom && (divs = document.querySelectorAll('div'), isAfterVdom = false);
start = performance.now();
each.call(divs, function(div) {
var width = div.clientWidth;
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
// Render result
renderSpeed(performance.now() - start);
};
// Access DOM at the next frame by requestAnimationFrame
rAF.onclick = function() {
isAfterVdom && (divs = document.querySelectorAll('div'), isAfterVdom = false);
start = performance.now();
each.call(divs, function(div) {
var width = div.clientWidth;
// Schedule the write operation to be run in the next frame.
raf(function() {
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
});
// Render result
raf(function() {
renderSpeed(performance.now() - start);
});
};
// Update the vDOM and access DOM just once by rAF
vdom.onclick = function() {
var sectCl = divCompartment.cloneNode(true),
divsCl = sectCl.querySelectorAll('div'),
dFrag = document.createDocumentFragment(),
width = divCompartment.querySelector('div').clientWidth;
isAfterVdom = true;
end = 0;
start = performance.now();
each.call(divsCl, function(div) {
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
dFrag.appendChild(sectCl);
divCompartment.parentNode.replaceChild(dFrag, divCompartment);
// Render result
renderSpeed(performance.now() - start);
};
html {
font: 14px Helvetica, sans-serif;
background: black;
color: white;
}
* {
box-sizing: border-box;
margin-bottom: 1rem;
}
h1 {
font-size: 2em;
//word-break: break-word;
-webkit-hyphens: auto;
}
button {
background-color: white;
}
div {
display: inline-block;
width: 5%;
margin: 3px;
background: white;
border: solid 2px white;
border-radius: 10px
}
section {
overflow: hidden;
}
#speed {
font-size: 2.4em;
}
Updating 1000 DOM Nodes