6

Consider these three versions of appending lis to a ul:

Naive Version (20% slower):

var ul = document.getElementById('targetUl');

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  ul.appendChild(li);
}

Using a JavaScript Fragment (4% slower):

var ul = document.getElementById('targetUl'),
    fragment = document.createDocumentFragment();

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  fragment.appendChild(li);
}
ul.appendChild(fragment);

Appending to an element not yet in the DOM (1.26% faster):

var ul = document.createElement('ul'),
    div = document.getElementById('targetDiv');

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  ul.appendChild(li);
}
div.appendChild(ul);

Why is appending to a DOM element held in memory faster than appending to a Fragment? Since fragment was created for this sole purpose shouldn't it be faster? Are they're any advantages to using a fragment over an element held in memory other than not having to include a top level element before appending?

Check the test output from jsperf: http://jsperf.com/javascript-fragments-tests

agconti
  • 15,820
  • 15
  • 69
  • 108
  • Without reference to the underlying code or design, whatever answer you get is guesswork. Mine is that a fragment is a generic container for any DOM element, whereas a UL is quite specific and can only have LI child elements, so there's work that a fragment must do when elements are appended that a UL doesn't. I would rate ±4% as not significant. – RobG Jul 22 '14 at 03:35
  • Don't worry too hard over micro optimization – Sterling Archer Jul 22 '14 at 03:36
  • What would make a Fragment faster? (Asking hypothetically what optimizations could be done in a Fragment vs. a regular DOM element) – JKillian Jul 22 '14 at 03:42
  • @JKillian Its supposed to be a minimal document object thats designed for this exact action. I'm not sure what optimizations they could have used to make this happen. – agconti Jul 22 '14 at 03:48

2 Answers2

8

It is a bit more work to insert multiple children from a document fragment (particularly when your test has 200 children) than it is to insert a single parent <ul> tag.

So, with the fragment, you're reparenting 200 <li> elements from the fragment to your <ul> tag in the DOM.

With your last code block, you're just reparenting the one <ul> tag by inserting it into the DOM.

So, in your particular example, using the document fragment creates more work for the insertion into the DOM than the way you run your last example that just has to insert the single <ul> tag.


The fragment has its tactical advantages when you want to keep track of a whole bunch of elements at the same level and then insert them with one line of code and you the parent is already in the DOM. But, it isn't always the fastest way to do things vs. a scenario where you can collect all the items out of the DOM at the same level under their actual parent node and then insert just that parent node.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • 1
    so why would anyone want to use a document fragment vs just creating the parent node of whatever tree of elements you plan to append to the document? what makes document fragments so special as opposed to just creating a parent element? – chiliNUT Jul 22 '14 at 03:41
  • 3
    @chiliNUT - sometimes the parent node is already in the DOM and you want to collect the child nodes outside the DOM and then insert them all at once. If that's the case, then using a fragment is less code because not using the fragment means you either have to keep track of an array of child nodes or create your own dummy parent and then you have to insert all the child nodes individually into the DOM. With the fragment, you can insert them all with one line of code. It's a programming convenience in some circumstances. – jfriend00 Jul 22 '14 at 03:44
  • Im not sure if I understand your answer. The 2nd test is doing more work than the 3rd because it has to reparenting a single element, even though that element has many children. Is it true that if I added a top level element to to the `li`s in the fragment, the performance would be the same, because then it would only be reparenting one element with many children? – agconti Jul 22 '14 at 03:45
  • @jfriend00 ah, that makes sense. This is interesting. I've got some code using document fragments that could probably be rewritten using a parent element. The criteria you have outlined are something I will need to consider in the future when dealing with dom node insertion. Thanks! – chiliNUT Jul 22 '14 at 03:46
  • 1
    @agconti - Inserting from a fragment is going to have performance proportional to the number of top level items in the fragment because that's how many items have to be reparented and inserted into the DOM one at a time. Child objects below those top level objects don't cost anything upon insertion (other than layout complexity when the one relayout is done) because they are attached to the parents so when the parent is moved, the children go with it for free (that's the advantage of the hierarchical nature of the DOM). – jfriend00 Jul 22 '14 at 03:49
  • @jfriend00 thats awesome. Is there any place that you recommend to learn more about the costs of reparenting, or even to a greater extent the advantages of the hierarchical nature of the DOM? – agconti Jul 22 '14 at 03:51
  • @chiliNUT—a fragment is handy if the nodes you want to insert must have a parent, e.g. when inserting table cells you don't necessarily want to create a row to append cells to, then loop over the row to insert the cells into the table. With a fragment you can append the cells to the fragment, then the fragment to the row in the DOM. – RobG Jul 22 '14 at 04:16
  • @agconti—jfriend00 is just presenting an opinion since there are no references to other sources to back up the assertions made. – RobG Jul 22 '14 at 04:19
  • In Firefox, `DocumentFragment` inherits from `FragmentOrElement` and also tacks on the `nsIDOMDocumentFragment` interface. Regular DOM elements implement the heavier `nsIDOMElement` interface. So there could be performance implications that would favor `DocumentFragment`, but I wouldn't be surprised if in reality there aren't. I know this isn't anything that definitive, but either way, looking about the FF source code is interesting and informative: http://mxr.mozilla.org/mozilla-beta/source/content/base/public/ – JKillian Jul 22 '14 at 04:54
0

My speculation is...

For Naive version. The loop when appending elements in the DOM gets bigger and longer. As more elements are loaded in the ul tag existing in the DOM the longer it takes to process the succeeding elements to be appended. This one takes longer because you are modifying a tag that already exists in the DOM.

For Javascript Fragment The fragment is non-existent in the HTML document yet and is only appended to ul after inserting a series of li in the fragment. There is only backend processing that is happening in the loop part then inserts the fragment that contains the multiple lis in the DOM. and ofcourse, since the ul is existing in the DOM there's still a loop involved because it takes some time to adopt the child.

For appending to an element not yet in DOM. This one mostly works on the backend and makes the UI interpreter exert less effort since it's like adding another paper on top of a pile of papers(Everything was worked on the paper to be added on top of the pile of papers).

My explanation might not be accurate but at least I'm sharing an idea. This has been one of the biggest problems of us Game developers.

Mike Ante
  • 706
  • 1
  • 6
  • 18