Inserting HTML Using Document.createDocumentFragment() Instead of using jQuery
Modern web pages update HTML or the DOM dynamically. This is one of the reasons why we have many large client-side JavaScript libraries and frameworks.
Unfortunately these same frameworks are often overkill for the requirements. The excessive JavaScript slows down the page load and run-time experience.
In most cases you can replace the use of these JavaScript libraries with a few lines of strategic code and native APIs.
One of these native JavaScript APIs is the createDocumentFragment method.
A few years ago I released the panorama.js library. It was written to NOT depend on jQuery or any other library or fast food framework.
Eliminating my need to depend on jQuery is an overall goal of mine. Today, my position is jQuery is obsolete since most of its features are native in browsers. The primary reason is the pursuit of better app performance and a shift to focusing on mobile.
jQuery was useful when inserting large chunks of DOM or HTML into a web page's DOM tree. Way back when I first started using jQuery this involved several lines to build a string of structured HTML elements and content.
Eventually I found, or the jQuery team added, the ability to pass in a markup string to jQuery that could then be inserted using one of the manipulation methods like appendTo, before, after, prepend, etc. You can find these and other DOM manipulation methods in the jQuery API documentation.
As I purged my jQuery dependency I needed to manipulate the DOM as I inserted and removed DOM nodes when the data changed the markup. I needed to figure out how to do this using the browser's native APIs.
Fortunately this much easier than it seems.
The CreateDocumentFragment Method
Somewhere in the past year I read and article or two about using the document.CreateDocumentFragment method. It allows you to create a section of markup, or DocumentFragment, and manipulate it before it is inserted in the page's DOM.
This is extremely helpful because most manipulations of DOM elements cause the browser to reflow and repaint the layout, which you want to reduce to a minimum.
This is because the nodes created by the CreateDocumentFragment method are not part of the DOM, they reside in memory.
The method returns a DocumentFragment object. This is the same object returned from a selector method like querySelector. It has the full battery of methods you can use for any element.
Normally when using CreateDocumentFragment the document.CreateElement and other manipulation methods are used to build and complete the fragment.
I knew there had to be a better way.
I started by rummaging through the jQuery source and while I could understand what they were doing, it just seemed like way too much code to accomplish a specific task.
One thing you learn when writing an abstraction library like jQuery is you need to architect methods to account for numerous scenarios, many of which are what I call 'edge cases' that just take up a lot of CPU cycles when not actually needed. Plus I doubt I will author code covered by those scenarios.
So I went on a safari to find a better way to leverage DocumentFragments. Thanks to a StackOverflow question and answer I had my method. The answer was exactly what I needed and extremely concise.
It consisted of a single helper method, called create that accepts a single string parameter, which of course is the HTML you want to insert in the DOM.
The big problem this function solves that I wanted to overcome is not adding a wrapping element around the desired markup. You can't just add a string of markup to a fragment, you have to add an HTML node.
The function creates a wrapping DIV element and adds the markup string to it using the DIV's innerHTML property. At this point you have a valid DIV element with child elements, assuming the string you passes has HTML in it.
function createFragment(htmlStr) { var frag = document.createDocumentFragment(), temp = document.createElement('div'); temp.innerHTML = htmlStr; while(temp.firstChild) { frag.appendChild(temp.firstChild); } return frag; }
You need to add the DIV's childNodes to the DocumentFragment. We don't want the DIV we created so instead the function loops through the DIV's childNodes and adds them to the DocumentFragment.
Now the HTML string you want to add to the page's DOM structure should be a valid node/nodeList that can be added to the DOM as you intended.
The function returns the DocumentFragment and it can be added using one of the node methods like appendChild.
// You can use native DOM methods to insert the fragment: document.body.appendChild(createFragment(htmlStr));
I created a jsFiddle to demonstrate how to accomplish all this.
I encourage you to learn to use the browser's native APIs because they are much more performant than using jQuery or any third party abstraction.
So I created a simple little test to compare the two techniques on jsPerf. As you can see the native technique is 10s of thousands of times faster than using jQuery.
I hope you find this technique useful. You don't have to be scared of backward compatibility issues because the CreateDocumentFragment function has been around since Internet Explorer 6, so there are no worries about compatibility and support.