Does using a document fragment really improve performance?

19,091

Solution 1

Document Fragment is much faster when it is used to insert a set of elements in multiple places. Most answers here point out its un-utility, but this is to demonstrate its strength.

Lets take an example.

Say we need to append 20 divs in 10 elements with class container.

Without:

var elements = [];
for(var i=20; i--;) elements.push(document.createElement("div"));

var e = document.getElementsByClassName("container");
for(var i=e.length; i--;) {
  for(var j=20; j--;) e[i].appendChild(elements[j].cloneNode(true));
}


With:

var frag = document.createDocumentFragment();
for(var i=20; i--;) frag.appendChild(document.createElement("div"));

var e = document.getElementsByClassName("container");
for(var i=e.length; i--;) e[i].appendChild(frag.cloneNode(true));

For me using a document fragment turns out to be 16 times faster on Chrome 48.

Test on JsPerf

Solution 2

Normally you'd want to use a fragment to avoid reflows (repainting the page). A good case would be if you were looping over something and appending within the loop, however, I think modern browsers optimize for this already.

I set up a jsPerf to illustrate a good example of when to use a fragment here. You'll notice in Chrome that there is hardly a difference (modern optimization at work, I presume), however, in IE7 I get .08 ops/sec without the fragment, 3.28 ops/sec with a fragment.

So, if you're looping over a large data set and appending A LOT of elements use a fragment instead so you only have one reflow. If you're only appending to the dom a few times or you're only targetting modern browsers it isn't necessary.

Solution 3

I've written a jspref to test this and it appears the node fragment is 2.34 % faster

http://jsperf.com/document-fragment-test-peluchetti

Share:
19,091

Related videos on Youtube

Sam Ccp
Author by

Sam Ccp

I'm a web developer, i am a standards freak and i love everything web. I consider myself a somewhat jquery expert and i'd like to learn with you and help in anything i can to get to a higher level of knowledge. Feel free to ask me anything, maybe i could help. Cheers

Updated on June 06, 2022

Comments

  • Sam Ccp
    Sam Ccp about 2 years

    I've got a doubt regarding performance in JS.

    Say, I've got the next code:

    var divContainer = document.createElement("div"); divContainer.id="container";
    var divHeader = document.createElement("div"); divHeader.id="header";
    var divData = document.createElement("div"); divData.id="data";
    var divFooter = document.createElement("div"); divFooter.id="footer";
    divContainer.appendChild( divHeader );
    divContainer.appendChild( divData );
    divContainer.appendChild( divFooter );
    document.getElementById("someElement").appendChild( divContainer );
    

    This code just creates the shell for some other functions to create a grid, the process to create the grid is very complex and with many validations and currently I'm using 2 methods to fill the grid, one creating the whole html in an array variable and the other one creating elements and appending them to a documentFragment.

    My question is if there's really an improvement regarding performance when using fragments, as I understand them - they manage elements on memory, so they're not attached to the document, thus, not triggering DOM recalculation and other nasty stuff. But the way I'm creating my variables, they're not attached to any DOM Element until i append the container to the actual page.

    So I was wondering if the previous code has better performance than using a document fragment that wraps it all like so:

    var fragment = document.createDocumentFragment();
    var divContainer = document.createElement("div"); divContainer.id="container";
    var divHeader = document.createElement("div"); divHeader.id="header";
    var divData = document.createElement("div"); divData.id="data";
    var divFooter = document.createElement("div"); divFooter.id="footer";
    divContainer.appendChild( divHeader );
    divContainer.appendChild( divData );
    divContainer.appendChild( divFooter );
    fragment.appendChild( divContainer )
    document.getElementById("someElement").appendChild( fragment.cloneNode(true) );
    

    As I've already said, this is a question regarding performance, I know that as a best practice it's recommended to use fragments, but I can't take the thought out of my head that doing that just creates a new object in memory and does nothing, so I assume that ditching the fragment in this case is valid.

    Hopefully some js guru / god will shine a light of hope in here and help us with this issue.


    Edit: So, I've been looking around for stuff related to this issue and it seems that documentFragments doesn't necessarily means better performance.

    It's just an "in memory" container of nodes. The difference between a fragment and say, a <div> is that the fragment has no parent and it will never be on the DOM, just in memory, which means that the operations made on the fragment are faster since there's no manipulation of the DOM.

    W3C's documentation on documentFragments is very vague but to the point and also, everybody's favorite browser does not uses real fragments, instead it creates a new document according to this MSDN documentation. Which means, fragments on IE are slower.

    So, the question prevails, if I create an element (a <div> for example) in a variable but DO NOT APPEND IT TO THE DOM, add elements (divs, tables, etc ) and stuff and after all the work has been done (loops, validations, styling of elements), that element is appended, is it the same as a fragment?

    Given the fact that IE uses a "fake" fragment I'd say at least in IE using that approach (using an element such as a div, not a fragment) is better, I really don't care for IE but I need to test it ( office's policy ).

    Also, if I create all the html on an array like so:

    var arrHTML = ["<table>","<tr>", ....]; 
    

    and then do this

    document.getElementById("someElement").innerHTML = arrHTML.join(""); 
    

    It's way faster on IE, but other major browsers ( FF, Chrome, Safari and Opera ) perform better when using a container and then appending it (fragment or div).

    All of this is because the process to create all the elements is done really fast, around 8 - 10 seconds to create up to 20,000 rows with 24 columns, it's a lot of elements / tags, but the browser seems to freeze a few seconds when they're all appended at once, if I try and append them one by one, it's hell.

    Thanks again folks, this is really interesting and fun.

    • jbabey
      jbabey over 11 years
      Stick it on jsperf and see which is faster on your target audience's browsers.
    • Sam Ccp
      Sam Ccp over 11 years
      I've used console's profile in firefox and chrome and they both executes in the same time ( around 180ms ) with real data in the header and data divs, i've tested the "vanilla" way without fragments, just appending all to a cointainer and that container append it to the DOM, also using fragments and creating the whole html on a variable and then adding an innerHTML to that variable, they all seem to work more or less the same, but my question is performance wise, which is better regarding memory and stuff like that. Thanks
    • Snuffleupagus
      Snuffleupagus over 11 years
      From what I understand you'd use a document fragment to avoid a lot of reflows, but if you're just appending a small amount (such as what you have shown) it really isn't worth it.
    • Felix Kling
      Felix Kling over 11 years
      The more elements you want to append, the greater the performance difference will be.
    • Sam Ccp
      Sam Ccp over 11 years
      Yeah, in this example i'm just appending 4 elements, but the process to create the grid could go from 1 row ( variable columns ) to 20,000 rows ( variable columns ) right now, the 2 methods are doing great, but i'm cleaning the code and this doubt is just eating my brains. Thanks to everyone interested in this.
    • Elliot B.
      Elliot B. over 8 years
      @jbabey jsPerf test results can be misleading. It's difficult to construct a jsPerf test that is truly representative of real-world DocumentFragment usage. The primary benefit of using DocumentFragments is that you avoid expensive page reflows and/or DOM traversal. None of the jsPerf tests here demonstrate the performance gains from avoiding page reflows and only the answer given by wolfram77 demonstrates the savings of avoiding extensive DOM traversal through the use of DocumentFragments.
  • Sam Ccp
    Sam Ccp over 11 years
    Thanks for that, testing it with firefox and safari both show that not using fragments is much faster, IE ( 8 ) just dies. But the question remains, Using Fragments is really better performant than just appending one element (with children in it, like the example) to the DOM?
  • Sam Ccp
    Sam Ccp over 11 years
    Yeah, i'm looping through a json object which varies in size, what i'm doing is creating an element in memory and appending stuff on each loop, and then that element is added to the page (DOM), so it's just one reflow instead of multiple ones, as i said, the method runs ok and it's really fast, both using fragments and creating the html in an array variable then adding it like this element.innerHTML = array.join(""); most browsers display the grid really fast and the worst scenario has been for 20,000 rows and it lasted 10 seconds. My doubt is if the fragment is really an improvement. Thanks!
  • Snuffleupagus
    Snuffleupagus over 11 years
    @SamCcp yerp, if you're not appending to the DOM within the loop a fragment isn't going to speed it up.
  • Sam Ccp
    Sam Ccp over 11 years
    Then another thing comes to mind, if you're appending elements to the DOM inside a loop, not even a fragment will help you, you're forcing the browser to do reflows right? then, what good are the fragments then? The plot thickens....
  • Snuffleupagus
    Snuffleupagus over 11 years
    @SamCcp You can just keep appending to the fragment then append the fragment to the DOM outside of the loop.
  • Nicola Peluchetti
    Nicola Peluchetti over 11 years
    @SamCcp in Firefox i have the same result as in chrome where there is no difference.
  • Sam Ccp
    Sam Ccp over 11 years
    Yeah, but it's the same if i just keep appending elements to another element in memory and then just append that one element to the DOM after the loop, ditching the fragment and just using elements. Right?
  • Sam Ccp
    Sam Ccp over 11 years
    Yeah, if you take a look at the browserscope from your jsperf, you'll notice that the best results are the ones without using fragments in all the browsers that have tested your jsperf, so based on that, i could say that using a fragment just slow things down, another thing to consider is that when the loop is too big, in IE ( 8 at least ) is better to use innerHTML than appending an element to the dom, fragment or not.
  • Snuffleupagus
    Snuffleupagus over 11 years
    @SamCcp Indeed, as long as its not actually being appended to the DOM it doesn't make much of a difference. I'd guess that a fragment would be very slightly faster, creating a DIV has a longer proto chain associated so I would imagine it would take slightly longer; but that would be in the realm of a few MS, not worth optimizing for. (DocumentFragment is directly under Node, an html element goes from HTMLSpanElement (or whatever relevant tag) to HTMLElement to Element to Node)
  • Sam Ccp
    Sam Ccp over 11 years
    Alrigh, we're getting somewhere, documentFragments are on top of the hierarchy because they're nodes, so they're better than elements since they're tags and are not parsed when appended?, sweet! But as you said before, it seems that the improvement is just by some ms. I'll keep investigating on this since i'm a resource freak. Thanks!
  • Akash Kava
    Akash Kava over 10 years
    For nodes upto 1000, there is anyway no visible performance improvement, not even in millseconds, it just adds up programming complexity in rewriting logic.
  • Pacerier
    Pacerier about 10 years
    @AkashKava, not true if no logic has been written yet and you are deciding which to use.
  • Pacerier
    Pacerier about 10 years
    It depends on what browser and what DOM element.
  • Akash Kava
    Akash Kava about 10 years
    @Pacerier I didn't get it, there is no evidence of fragment helps in anyway, it is theoretical. Instead can you provide one single evidence?
  • Pacerier
    Pacerier about 10 years
    @AkashKava, I didn't say using fragment is more performant than using div. I only said "adds up programming complexity" is not true.
  • Andreas
    Andreas about 9 years
    any body notice below the chart - there exist metrics! For this it's operations per second (higher is better) LOL eg No fragment is superior
  • Elliot B.
    Elliot B. over 8 years
    @SamCcp This jsPerf test is flawed. The point of DocumentFragments is to avoid expensive page reflows and/or DOM traversals. This jsPerf test incorporates neither. The DOM manipulations performed by the jsPerf test are not visible and there are no page reflows.
  • Elliot B.
    Elliot B. over 8 years
    This is the best answer. While the jsPerf test here does not incorporate page reflows (avoiding page reflows is the primary benefit of using DocumentFragments), it does illustrate the real performance gains in using DocumentFragments to avoid DOM traversal. The DocumentFragment code performs 2x as fast as the non-fragment code in Chrome 48 and nearly 4x as fast in IE11. If you are a developer who builds pages that render dynamically with JavaScript, this is why you use DocumentFragments.
  • wolfram77
    wolfram77 over 8 years
    Thanks! Yet to learn on page reflow, and when it occurs. This seems to be well detailed.
  • AntonB
    AntonB about 8 years
    @wolfram77 Why are you deep cloning the fragment? Couldn't you just appendChild(frag), works on all browsers.
  • wolfram77
    wolfram77 about 8 years
    @AntonB I tried what you suggested, but it seems document fragments behave in the same way as normal elements, i.e. they get removed from the source and get added to the destination. However unlike elements, the contents of document fragment gets added, instead of the whole fragment, like it happens in case of an normal element, thus making it possible to add multiple elements in one go.
  • wolfram77
    wolfram77 about 8 years
    @AntonB you could just use appendChild(frag) only if you intention is to use the fragment just once.
  • swajak
    swajak almost 7 years
    you are nearly doubling the string size in 'has fragment', by doing +'fragment' 😬
  • Sam Ccp
    Sam Ccp over 6 years
    on this results, the NoFragment destroys the fragment one, i can see your point on the repaint on the browser, but if you append just one element, there's only one repaint to be done. So this is negligible IMHO.
  • Admin
    Admin about 5 years
    Both make reflow once on same platform, but maybe the cloneNode(true) is the flaw?
  • Admin
    Admin about 5 years
    If expensive page reflows and/or DOM traversals has to be done, then always take out the element and do it in memory. When inserted it reflows once.
  • trusktr
    trusktr over 4 years
    link is borken.
  • trusktr
    trusktr over 4 years
    Wait, wolfram77 is showing that with the fragment, the extra loop is indeed not needed. This shows a use case where with fragment is faster. The loop is not needed with the fragment, because using cloneNode(true) clones all the children, and thus looping is not required.
  • Smart Coder
    Smart Coder about 3 years
    the result is right.but you should not add +'fragment'.This is called the controlled variable