jQuery Appends Multiple Elements Using Efficient Document Fragments

Posted November 1, 2011 at 2:26 PM by Ben Nadel

Tags: Javascript / DHTML

Earlier today, in my blog post on using jQuery's $.map() method, I talked about creating a buffer of detached DOM (Document Object Model) nodes. The value-add behind that post was the fact that the DOM nodes remained detached as long as possible; this allows them to be created and configured without incurring the cost of DOM rendering and repainting. As a follow-up to that concept, I wanted to do a little debugging in the jQuery library to see how the DOM node buffer was actually being appended to the rendered document.

The jQuery library is dense! While I have become pretty good at finding things within it, I am still quite an amateur when it comes to actually following the code flow and logic. Based on some experiments that John Resig performed a few years ago, it makes sense that the library would make use of Document Fragments to improve performance. But, I couldn't tell you from looking at the code whether or not this is true.

So, I decided to put some logging in the library to help clarify the situation. In the following demo, I'm simply creating an array of detached DOM nodes and then inserting them into the rendered document.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Testing jQuery's Use Of Document Fragments</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Testing jQuery's Use Of Document Fragments
  • </h1>
  •  
  • <p class="nodes">
  • <!-- To be populated dynamically. -->
  • </p>
  •  
  •  
  • <script type="text/javascript" src="./jquery-1.7temp.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Get a reference to the parent element.
  • var nodes = $( "p.nodes" );
  •  
  • // Create a buffer of detached DOM nodes.
  • var nodeBuffer = [
  • document.createTextNode( "Hello " ),
  • document.createTextNode( "my " ),
  • document.createTextNode( "love." )
  • ];
  •  
  • // Append the DOM buffer to the parent.
  • nodes.append( nodeBuffer );
  •  
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, our demo creates an array with three detached Text Nodes. This array is then passed to the append() method which, subsequently, inserts it into the rendered DOM tree.

But, how does the insertion take place? To figure this out, I opened up the jQuery library, found the append() method definition, and added some debug code:

  • append: function() {
  • return this.domManip(arguments, true, function( elem ) {
  •  
  •  
  • // -- BEGIN: Debug. ---------- //
  • // --------------------------- //
  •  
  • // Check the type of target node that is be appending
  • // to the current node.
  • console.log( "Append:", elem.nodeType );
  •  
  • // Check to see if this is a fragment.
  • console.log(
  • "Is Fragment:",
  • (document.createDocumentFragment().nodeType == elem.nodeType)
  • );
  •  
  • // Check the length of the element.
  • console.log(
  • "Length:",
  • elem.childNodes.length
  • );
  •  
  • // Dump the fragments.
  • console.log( elem.childNodes[ 0 ] );
  • console.log( elem.childNodes[ 1 ] );
  • console.log( elem.childNodes[ 2 ] );
  •  
  • // --------------------------- //
  • // -- END: Debug. ------------ //
  •  
  •  
  • if ( this.nodeType === 1 ) {
  • this.appendChild( elem );
  • }
  • });
  • }

As you can see, this debug code is examining the type and contents of the element being inserted into the parent DOM node. When we run the above demo with the embedded debug code, we get the following console output:

Append: 11
Is Fragment: true
Length: 3
<TextNode textContent="Hello ">
<TextNode textContent="my ">
<TextNode textContent="love.">

As you can see, the element being inserted is a Document Fragment (nodeType 11). This Document Fragment is a lightweight node implementation that contains the three Text Nodes we created in our demo. However, since we passed in an array of nodes, it is safe to say that jQuery created and populated this fragment on our behalf (for performance reasons).

Cool stuff! The take-away here is that if we create detached node buffers, jQuery will optimize the insertion using fragments. In other words, the benefit of delaying DOM rendering and repainting is maintained through the whole process.




Reader Comments

Nov 1, 2011 at 8:32 PM // reply »
12 Comments

Normally when I have to generate something simple like the list in your example I use $.map to create a array of strings and then follow up by $(...).append(array_of_strings.join()).

Is the only reason for creating document fragments that you can manipulate them with jQuery? Or are there also other concerns ?


Nov 2, 2011 at 6:02 PM // reply »
10,743 Comments

@Morten,

Yeah, the only reason I would do it this way (as opposed to an Array of string fragments) is if I wanted to use jQuery collections to be able to modify the elements before they were inserted into the document. If all I needed to do was template-string replacements (which this demo could have ultimately done), then I could have just used .replace() to "configure" the elements in a string-only buffer.

So yeah, this approach is for jQuery-based manipulation (in my mind).


Nov 3, 2011 at 7:23 AM // reply »
12 Comments

@Ben,

Yeah, I thought as much. Do you know of any benchmarks on using 'jQuery', '.replace()', 'string concatenation' -based manipulation?


Nov 7, 2011 at 10:01 AM // reply »
10,743 Comments

@Morten,

I don't really know of any comparison. Plus, a comparison might not be all that great when it comes to templating systems. I say this because I believe that most templating systems actually compile their templates down to inline JavaScript code rather than .replace() methods. So, there is a large up-front compile cost; but then creating template instances I believe is super fast.

Of course, speed here is all relative; I am sure that every approach is fast when it comes to what the browser can handle, until you get into massive amounts of data or something???


Nov 7, 2011 at 10:30 AM // reply »
12 Comments

@Ben,
Yeah, your probably right - what ever solution you chose caching should be involved.
I fail to see an HTML/Javascript app having a template usage that it would seriously effect your experience?

I guess things like this is more of a problem when done server-side than client-side...


Nov 7, 2011 at 10:38 AM // reply »
10,743 Comments

@Morten,

I tend to agree with you. Most of this stuff is going to be pretty fast!


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2012 at 1:58 AM
Updated: Converting A ColdFusion Query To CSV Using QueryToCSV()
Hi Ben, why do you need to have so many double quotes when adding the field and field name to the row data? ----------------------------------------- <cfset LOCAL.RowData[ LOCAL.ColumnIndex ] = ... read »
AXL
May 21, 2012 at 1:24 AM
URL Rewriting And ColdFusion's WriteToBrowser Image Functionality (CFFileServlet)
@Mounir, Open your lower case URL Rewrite rule and add the following condition. Condition input: {REQUEST_URI} Check if input string: Does Not Match the Pattern Pattern: ^/CFFileServlet/_cf_ca ... read »
May 20, 2012 at 4:28 AM
Understanding The Complex And Circular Relationships Between Objects In JavaScript
@Will Vaughn I tried your javascript example but got this error:- foo.print is not a function ... read »
May 19, 2012 at 5:37 AM
A Graphical Explanation Of Javascript Closures In A jQuery Context
Thanks for this article, but I fear you missed an important point. If variables in the outer context change, these changes affect the inner anonymous functions as well. That means: if you change the ... read »
May 18, 2012 at 3:39 PM
Parsing CSV Data With An Input Stream And A Finite State Machine
Can you use file upload button with this? and read live? or does the file have to already be on the server saved? ... read »
May 18, 2012 at 1:06 AM
VIRGO (Aug. 23-Sept. 22): Dead On The Money!
A friend of mine and I were arguing about astrology and she told me that he believes in astrology. She hasn't provided me with any evidence that the belief makes any sense to me. She she been telling ... read »
May 17, 2012 at 11:32 PM
Using ColdFusion to Handle 404 Errors (Page Not Found) On Development Server
Very easy the configuration. I read a lot pages and I can't find the solution. I open the administrator and change this Administrator/server settings/Error Handlers/Missing Template Handler and p ... read »
May 17, 2012 at 3:13 PM
LOCAL Variables Scope Conflicts With ColdFusion Query of Queries
I never cease to be amazed that almost EVERY random CF issue I come across lands me on your site. Thank you for documenting your findings for the world. ... read »