using document.createDocumentFragment() and innerHTML to manipulate a DOM
Solution 1
You can't set the innerHTML of a document fragment like you would do with a normal node, that's the problem. Adding a standard div and setting the innerHTML of that is the common solution.
Solution 2
While DocumentFragment
does not support innerHTML
, <template>
does.
The content
property of a <template>
element is a DocumentFragment
so it behaves the same way. For example, you can do:
var tpl = document.createElement('template');
tpl.innerHTML = '<tr><td>Hello</td><td>world</td></tr>';
document.querySelector('table').appendChild(tpl.content);
The above example is important because you could not do this with innerHTML
and e.g. a <div>
, because a <div>
does not allow <tr>
elements as children.
NOTE: A DocumentFragment
will still strip the <head>
and <body>
tags, so it won't do what you want either. You really need to create a whole new Document
.
Solution 3
DocumentFragment
inherits from Node
, but not from Element
that contains the .innerHTML
property.
In your case I would use the <template>
tag. In inherits from Element
and it has a nifty HTMLTemplateElement.content
property that gives you a DocumentFragment
.
Here's a simple helpermethod you could use:
export default function StringToFragment(string) {
var renderer = document.createElement('template');
renderer.innerHTML = string;
return renderer.content;
}
Solution 4
I know this question is old, but I ran into the same issue while playing with a document fragment because I didn't realize that I had to append a div to it and use the div's innerHTML
to load strings of HTML in and get DOM Elements from it. I've got other answers on how to do this sort of thing, better suited for whole documents.
In firefox (23.0.1) it appears that setting the innerHTML property of the document fragment doesn't automatically generate the elements. It is only after appending the fragment to the document that the elements are created.
To create a whole document use the document.implementation
methods if they're supported. I've had success doing this on Firefox, I haven't really tested it out on other browsers though. You can look at HTMLParser.js in the AtropaToolbox for an example of using document.implementation
methods. I've used this bit of script to XMLHttpRequest
pages and manipulate them or extract data from them. Scripts in the page are not executed though, which is what I wanted though it may not be what you want. The reason I went with this rather verbose method instead of trying to use the parsing available from the XMLHttpRequest
object directly was that I ran into quite a bit of trouble with parsing errors at the time and I wanted to specify that the doc should be parsed as HTML 4 Transitional because it seems to take all kinds of slop and produce a DOM.
There is also a DOMParser
available which may be easier for you to use. There is an implementation by Eli Grey on the page at MDN for browsers that don't have the DOMParser
but do support document.implementation.createHTMLDocument
. The specs for DOMParser
specify that scripts in the page are not executed and the contents of noscript tags be rendered.
If you really need scripts enabled in the page you could create an iFrame with 0 height, 0 width, no borders, etc. It would still be in the page but you could hide it pretty well.
There's also the option of using window.open()
with document.write
, DOM methods or whatever you like. Some browsers even let you do data URI's now.
var x = window.open( 'data:text/html;base64,' + btoa('<h1>hi</h1>') );
// wait for the document to load. It only takes a few milliseconds
// but we'll wait for 5 seconds so you can watch the child window
// change.
setTimeout(function () {
console.log(x.document.documentElement.outerHTML);
x.console.log('this is the console in the child window');
x.document.body.innerHTML = 'oh wow';
}, 5000);
So, you do have a few options for creating whole documents offscreen/hidden and manipulating them, all of which support loading the document from strings.
There's also phantomjs, an awesome project producing a headless scriptable web browser based on webkit. You'll have access to the local filesystem and be able to do pretty much whatever you want. I don't really know what you're trying to accomplish with your full page scripting and manipulation.
Solution 5
For a Firefox add-on, it probably makes more sense to use the document.implementation.createHTMLDocument
method, and then go from the DOM that gives you.
Loic Duros
Updated on July 09, 2022Comments
-
Loic Duros almost 2 years
I'm creating a document fragment as follow:
var aWholeHTMLDocument = '<!doctype html> <html><head></head><body><h1>hello world</h1></body></html>'; var frag = document.createDocumentFragment(); frag.innerHTML = aWholeHTMLDocument;
The variable
aWholeHTMLDocument
contains a long string that is the entire html document of a page, and I want to insert it inside my fragment in order to generate and manipulate the DOM dynamically.My question is, once I have added that string to
frag.innerHTML
, shouldn't it load this string and convert it to a DOM object?After setting innerHTML, shouldn't I have access to the DOM through a property?
I tried frag.childNodes but it doesn't seem to contain anything, and all I want is to just access that newly created DOM.
-
Pointy over 12 yearsI'm not sure that it's possible for the "innerHTML" of any DOM element (and a document fragment is really just a DOM element that can't be part of the DOM) to be a complete HTML document. The answer to your question would be "yes" if it were possible.
-
Admin almost 5 yearsInstead of .appendChild(frag) you do .innerHTML once for all. It is faster than creating document fragment, because String handling is faster.
-
Admin almost 5 years
-
-
Loic Duros over 12 yearsSo what you are saying is that I should do the following? var aWholeHTMLDocument = '<!doctype html> <html><head></head><body><h1>hello world</h1></body></html>'; var div = document.createElement('div'); div.innerHTML = aWholeHTMLDocument; This will strip everything except the <h1> though...
-
Loic Duros over 12 yearsThis is for a Firefox add-on. I'm placing aWholeHTMLDocument into an iFrame, but I need it whole, not just the <body> tags, the <head> tag also contains stuff of interest, and I want to keep the whole DOM intact.
-
qw3n over 12 years@Loic then what you want is to get a reference to the
iframe
and set it's innerHTML toaWholeHTMLDocument
-
ˈvɔlə about 10 years@Loic - He never said something like this ... He just explained your mistake. Your suggested solution in your comment is absolutely not recommended.
-
Jorge Fuentes González about 6 yearsThis not addresses the "process string" problem.
-
connexo almost 3 yearsReally, this should be the accepted answer, as it also provides a very good solution. @Chowey:
tr
elements inside atable
live in eitherthead
,tbody
ortfoot
. -
borie88 about 2 yearsThis is the only answer I found that does not add a wrapper div around the content being inserted. Super helpful when programmatically inserting html content into the slot of a web component