3

I gather that the whole point of a DocumentFragment is to be able to construct the contents without touching the DOM until it’s ready to go.

Given that DocumentFragment doesn’t support innerHTML, it can be a bit tedious. On the other hand, once constructed, it’s easy to add the contents to an existing DOM node by the fragment itself.

If I create a div without adding it to the DOM, I can populate it how I like, including innerHTML. As far as I can tell, it should have no additional impact on performance.

Is there a simple way (ie in one line or so) to copy the contents of an existing DOM node to a DocumentFragment? The process would look like:

var div=document.createElement('div');
var fragment=document.createDocumentFragment();
div.innerHTML='…';
//  copy contents to fragment
//  etc

This way I could have the best of both worlds.

Answer

Here is the answer by @KevBot below incorporated into the example:

var divTest=document.querySelector('div#test');

var html='<p>One</p><p>Two</p>';
var fragment=document.createRange().createContextualFragment(html);

divTest.appendChild(fragment);
Manngo
  • 14,066
  • 10
  • 88
  • 110
  • I like this question some of these solutions implement some obscure JavaScript. – zer00ne Jan 21 '17 at 02:42
  • *If I create a div without adding it to the DOM, I can populate it how I like, including innerHTML. As far as I can tell, it should have no additional impact on performance.* The difference between a div and a docFrag is that when you append a div to the DOM, it stays there with everything you added to it. With a docFrag, you append it to the DOM and everthing you added to it is in the DOM but docFrag isn't. Kinda like that tablecloth trick when you yank the tablecloth out but leave the plates on the table. – zer00ne Jan 21 '17 at 05:13

4 Answers4

8

Yes, you can easily create a fragment with a string of HTML using the document.createRange method.

document.createRange returns a Range object, which has a method called createContextualFragment which allows you to get a fragment from just HTML.

function fragmentFromString(strHTML) {
  return document.createRange().createContextualFragment(strHTML);
}

let div = document.createElement('div');
div.innerHTML = '<p>Testing</p>';

let fragment = fragmentFromString(div.innerHTML);
console.log(fragment);
<div>
  <p>Random Content</p>
</div>

Works in all major browsers and IE11.

KevBot
  • 17,900
  • 5
  • 50
  • 68
  • Thanks for the answer. The fact that it works in IE11 is cutting it a bit fine, but who cares about the rest anyway? I have amended my question example to reflect your example. – Manngo Jan 21 '17 at 05:33
  • You're welcome. Yeah, even microsoft has abandoned support for ie10 and below at this point in time. – KevBot Jan 21 '17 at 05:47
5

You can create a <template> element, which is a document fragment; set .innerHTML of template element, and get .innerHTML of template element using .content property

var template = document.createElement("template");
var div = document.createElement("div");
div.innerHTML = "<p>abc</p>";
template.innerHTML = div.innerHTML;
document.body.appendChild(template.content);
guest271314
  • 1
  • 15
  • 104
  • 177
  • For me, it was a much better solution. I was building a bookmark and I needed to grab DOM, clean it up and save it as text. With this, I was able to use querySelectorAll to remove what I didn't need and get text using `template.innerHTML` without touching actual DOM. – jcubic Dec 25 '22 at 21:02
1

You can use appendChild to add the new element to the fragment.

var div=document.createElement('div');
var fragment=document.createDocumentFragment();
div.innerHTML='…';

fragment.appendChild(div);

If you only want to inject the contents of the element into the fragment then you can iterate over the childNodes and insert them into the fragment.

div.childNodes.forEach(function(node) {
    fragment.appendChild(node);
});
Sushanth --
  • 55,259
  • 9
  • 66
  • 105
  • Thanks for the answer, but I don’t want the `div` itself. Just the contents. – Manngo Jan 21 '17 at 01:41
  • You could use the `childNodes` property and iterate over each node and inject it into the fragment. `div.childNodes.forEach(function(node) { fragment.appendChild(node); });` – Sushanth -- Jan 21 '17 at 01:45
1

Try these out for size

text = frag.appendChild( document.createTextNode( "insert text here" ) )

or

text = frag.appendChild( document.createTextNode( div.innerHTML ) )

or

text = frag.appendChild( document.createTextNode( div.textContent ) )

The text variable will hold the textnode while you have also appended the textnode all in one line

Jason
  • 779
  • 1
  • 9
  • 30