Convert String to DOM Nodes

By  on  

My original post featured DOMParser, a JavaScript API for converting HTML strings into DOM nodes. While DOMParser works well in most cases, that API does have some rough edges and isn't as performant as another API: ContextualFragment. I've rewritten this post to highlight ContextualFragment, but if you still care to learn about DOMParser, please see the original text at the bottom of this post.

It wasn't too long ago that browsers were mostly stagnant when it came to implementing new APIs and features, leading to the rise of MooTools (FTW), jQuery, Dojo Toolkit, Prototype, and likewise JavaScript toolkits. Then we started doing more client side rendering and were forced to use a variety of tricks to handle templates, including massive HTML strings in our JavaScript and even abusing <script> tags to hold our templates.

Of course after you've placed your content into the template, you then need to turn that string into DOM nodes, and that process had a few of its own tricks, like creating an offscreen, dummy <div>, setting its innerHTML to the string value, grabbing the firstChild, and moving the node to its desired node. Each JavaScript toolkit would use its own strategy for converting string to DOM, highlighting the need for a standard method to accomplish this task.

Today there's a little known (but standard) way for converting string to DOM with JavaScript: ContextualFragment.

I've touched on DocumentFragment to create and store DOM nodes for performance in the past, but that post illustrated element creation via document.createElement:

// Use a DocumentFragment to store and then mass inject a list of DOM nodes
var frag = document.createDocumentFragment();
for(var x = 0; x < 10; x++) {
	var li = document.createElement("li");
	li.innerHTML = "List item " + x;
	frag.appendChild(li);
}

To create DOM nodes from a string of HTML we'll use document.createRange().createContextualFragment:

let frag = document.createRange().createContextualFragment('
One
Two
'); console.log(frag); /* #document-fragment
One
Two
*/

DocumentFragment objects share most of the methods that NodeList objects have, so you can use typical DOM methods like querySelector and querySelectorAll as well DOM traversal properties like firstChild with the resulting DocumentFragment:

let firstChild = frag.firstChild;

let firstDiv = frag.querySelector('div');
let allDivs = frag.querySelectorAll('div');

When you're ready to inject all of the created DOM nodes, you can simply execute:

// "placementNode" will be the parent of the nodes within the DocumentFragment
placementNode.appendChild(frag);

You can also inject nodes one at a time:

placementNode.appendChild(frag.firstChild);

The document.createRange().createContextualFragment function is an awesome, sane method for converting strings to DOM nodes within JavaScript. Ditch your old shims and switch to this performant, simple API!

The Original Post: DOMParser

Today we have a standard way for converting string to DOM with JavaScript: DOMParser.

The JavaScript

All you need to do is create a DOMParser instance and use its parseFromString method:

let doc = new DOMParser().parseFromString('<div><b>Hello!</b></div>', 'text/html');

Returned is a document containing the nodes generated from your string. With said document you can use standard node traversal methods to retrieve the nodes we specified in our string:

let doc = new DOMParser().parseFromString('<div><b>Hello!</b></div>', 'text/html');
let div = doc.body.firstChild;

let divs = doc.body.querySelectorAll('div');

You don't need a single wrapping element like JSX components -- you can have sibling elements:

let doc = new DOMParser().parseFromString('<div>1</div><div>2</div>', 'text/html');
let firstDiv = doc.body.firstChild;
let secondDiv = firstDiv.nextSibling;

Here's a simple wrapping function for DOMParser to retrieve the nodes:

let getNodes = str => new DOMParser().parseFromString(str, 'text/html').body.childNodes;

let nodes = getNodes('<div>1</div><div>2</div>');

// [div, div] 

The DOMParser object is an awesome, sane method for converting strings to DOM nodes within JavaScript. Ditch your old shims and switch to this efficient, simple API!

Recent Features

  • By
    Send Text Messages with PHP

    Kids these days, I tell ya.  All they care about is the technology.  The video games.  The bottled water.  Oh, and the texting, always the texting.  Back in my day, all we had was...OK, I had all of these things too.  But I still don't get...

  • By
    5 More HTML5 APIs You Didn&#8217;t Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

Incredible Demos

Discussion

  1. parser

    Can’t you do this like 100 times faster and easier with jQuery using

    $('Hello!')
    • _xxx

      with jQuery <- exactly the problem with your argument. not to mention pretty big difference in performance.

    • Thats the whole point of this article — present a native, free of libraries way of doing it.

  2. Hristo Chakarov

    Some old technique:

    var tmp = document.createElement( 'div' );
    tmp.innerHTML = 'Hello!';
    var elements = tmp.childNodes;
    
    • Bobby

      Thank you for sharing this technique! Definitely learn something new.

  3. Danny Engelman
    • David Baker

      I tried running those performance tests and DOMParser came out the slowest for both. Am I missing something?

      Link to test results: http://imgur.com/a/scy0Q

  4. Owen

    How do I get the HTML back out (for example, what if the html that went in has a style-sheet)? Using doc.body.innerHTML doesn’t have that or any header or other information in it… I want all the marked-up text of the whole document…

  5. Owen

    I was able to get all of the html by wrapping the string with body tags. Sort of a kludge, but it works for what I need…

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!