115

I was reading about document fragments and DOM reflow and wondered how document.createDocumentFragment differed from document.createElement as it looks like neither of them exist in the DOM until I append them to a DOM element.

I did a test (below) and all of them took exactly the same amount of time (about 95ms). At a guess this could possibly be due to there being no style applied to any of the elements, so no reflow maybe.

Anyway, based on the example below, why should I use createDocumentFragment instead of createElement when inserting into the DOM and whats the differnce between the two.

var htmz = "<ul>";
for (var i = 0; i < 2001; i++) {
    htmz += '<li><a href="#">link ' + i + '</a></li>';
}
htmz += '<ul>';

//createDocumentFragment
console.time('first');
var div = document.createElement("div");
div.innerHTML = htmz;
var fragment = document.createDocumentFragment();
while (div.firstChild) {
    fragment.appendChild(div.firstChild);
}
$('#first').append(fragment);
console.timeEnd('first');

//createElement
console.time('second');
var span = document.createElement("span");
span.innerHTML = htmz;
$('#second').append(span);
console.timeEnd('second');


//jQuery
console.time('third');
$('#third').append(htmz);
console.timeEnd('third');
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
screenm0nkey
  • 18,405
  • 17
  • 57
  • 75

3 Answers3

113

The difference is that a document fragment effectively disappears when you add it to the DOM. What happens is that all the child nodes of the document fragment are inserted at the location in the DOM where you insert the document fragment and the document fragment itself is not inserted. The fragment itself continues to exist but now has no children.

This allows you to insert multiple nodes into the DOM at the same time:

var frag = document.createDocumentFragment();
var textNode = frag.appendChild(document.createTextNode("Some text"));
var br = frag.appendChild(document.createElement("br"));
var body = document.body;
body.appendChild(frag);
alert(body.lastChild.tagName); // "BR"
alert(body.lastChild.previousSibling.data); // "Some text"
alert(frag.hasChildNodes()); // false
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 3
    Thanks for the response. You say that it allows for multiple insert at the same time but I can achieve that using doc.createElement. The only difference being I had to wrap the elements in a tag first and then insert that into the DOM. Is that why I should use createDocumentFragment? To avoid unnecessary elements being inserted into the DOM? – screenm0nkey Aug 03 '10 at 15:36
  • 4
    Yes, that's definitely one reason to use it. Also, a document fragment may contain any kind of node whereas an element may not. – Tim Down Aug 03 '10 at 15:58
  • 4
    Actually the fragment does not "disappear"; the browser moves the its childNodes to the DOM. So the fragment will still exist, but it will be empty after the insertion. – inf3rno Aug 03 '14 at 11:40
  • 2
    @inf3rno: Hence my use of the word "effectively" and the explanation that follows, which is much the same as yours. I concede that I should have said explicitly in the answer that the fragment continues to exist with no child nodes. – Tim Down Aug 03 '14 at 13:35
  • 1
    @TimDown thanks for your answer! If i get your point right, regarding performance, use `createFragment` and `createElement` in memory has roughly the same effect as they both ***batch*** updated the DOM instead of ***iteratively*** update for multiple times. While the main benefit of `createFragment` is that it offers you the flexibility to choose whatever child elements to append for free? Correct me if i were wrong. – Allen Feb 24 '17 at 06:26
  • Consider the DocumentFragment as a temporary host parent element for multiple children elements. – Omri Luzon Feb 01 '21 at 08:10
16

Another very important difference between creating an element and a document fragment:

When you create an element and append it to the DOM, the element is appended to the DOM, as well as the children.

With a document fragment, only the children are appended.

Take the case of:

var ul = document.getElementById("ul_test");


// First. add a document fragment:


(function() {
  var frag = document.createDocumentFragment();
  
  
  var li = document.createElement("li");
  li.appendChild(document.createTextNode("Document Fragment"));
  frag.appendChild(li);
  
  ul.appendChild(frag);
  console.log(2);
}());

(function() {
  var div = document.createElement("div");
  
  
  var li = document.createElement("li");
  li.appendChild(document.createTextNode("Inside Div"));
   div.appendChild(li);
  
  ul.appendChild(div);
}());
Sample List:
<ul id="ul_test"></ul>

which results in this malformed HTML (whitespace added)

<ul id="ul_test">
  <li>Document Fragment</li>
  <div><li>Inside Div</li></div>
</ul>
Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
1

You can think of a DocumentFragment as a virtual DOM. It's not connected to the DOM and unlike elements, it has no parent, EVER. You can then interact with the fragment as if it's a virtual document object. It's all in memory.

It's really helpful to use fragments when you have many DOM manipulations to make or style changes, because those will trigger reflows and repaints - expensive operations on the DOM that can slow the page load down.

The bonus you get with fragment is that it triggers only one reflow when the fragment is inserted into the DOM, no matter how many children it contains.

DocumentFragment is not an element or a Node. It's a stripped down Document object with a reduced set of properties and methods.

If you've ever heard of the virtual DOM with React, they are making heavy use of DocumentFragments in the ReactDOM library. That's why it's so performant.

Charles Owen
  • 2,403
  • 1
  • 14
  • 25