I was playing around with DOM DocumentFragments recently, in JavaScript, seeing what I could make with them. Roughly speaking, a DocumentFragment is a lightweight container that can hold DOM nodes. It’s part of the DOM 1 specification and is supported in all modern browsers (it was added to Internet Explorer in version 6).
In reading up on them I came across an interesting point, from the specification:
Furthermore, various operations — such as inserting nodes as children of another Node — may take
DocumentFragment
objects as arguments; this results in all the child nodes of theDocumentFragment
being moved to the child list of this node.
This means that if you take a bunch of DOM nodes and append them to a fragment then you can simply append the fragment to the document, instead (and achieve the same result – as if you had appended each node individually). I instantly smelled a possible performance improvement here. I investigated a little bit further and noticed that DocumentFragments support the cloneNode
method, as well. This provides all the functionality that you need to highly-optimize your DOM insertion code.
I set up a simple demo to test the theory.
Let’s take the situation where you have a bunch of DOM nodes that you need to append into the document (in the demo it’s 12 nodes – 8 at the top level – against a whole mess of divs).
-
var elems = [
-
document.createElement("hr"),
-
text( document.createElement("b"), "Links:" ),
-
document.createTextNode(" "),
-
text( document.createElement("a"), "Link A" ),
-
document.createTextNode(" | "),
-
text( document.createElement("a"), "Link B" ),
-
document.createTextNode(" | "),
-
text( document.createElement("a"), "Link C" )
-
];
-
-
function text(node, txt){
-
node.appendChild( document.createTextNode(txt) );
-
return node;
-
}
Normal Append
If we wanted to append these nodes into the document we would probably do it in this traditional manner: Looping through the nodes and cloning them individually (so that we can continue to append them all throughout the document).
-
var div = document.getElementsByTagName("div");
-
-
for ( var i = 0; i < div.length; i++ ) {
-
for ( var e = 0; e < elems.length; e++ ) {
-
div[i].appendChild( elems[e].cloneNode(true) );
-
}
-
}
DocumentFragment Append
However, when we bring DocumentFragments into the picture we can immediately see a different structure. To start we append all our nodes into the fragment itself (built using the createDocumentFragment
method).
But the interesting point comes when it’s time to actually insert the nodes into the document: We only have to call appendChild
and cloneNode
once for all the nodes!
-
var div = document.getElementsByTagName("div");
-
-
var fragment = document.createDocumentFragment();
-
for ( var e = 0; e < elems.length; e++ ) {
-
fragment.appendChild( elems[e] );
-
}
-
-
for ( var i = 0; i < div.length; i++ ) {
-
div[i].appendChild( fragment.cloneNode(true) );
-
}
Setting some time stamps we can see our results pay off in spades:
Browser | Normal (ms) | Fragment (ms) |
---|---|---|
Firefox 3.0.1 | 90 | 47 |
Safari 3.1.2 | 156 | 44 |
Opera 9.51 | 208 | 95 |
IE 6 | 401 | 140 |
IE 7 | 230 | 61 |
IE 8b1 | 120 | 40 |
As it turns out: A method that is largely ignored in modern web development can provide some serious (2-3x) performance improvements to your DOM manipulation.