Picking up after @dandavis' updated answer using the createContextualFragment()
, a few people pointed out the small limitation (see here) that certain context-sensitive elements require their parent to be present otherwise this function will discard them (i.e. tr
, td
, thead
, caption
, etc).
Most realistic alternative solutions revolve around doing this through the <template>
element in some fashion.
Given let htmlStr = '<td></td>'
<template>
let temp = document.createElement("template");
temp.innerHTML = htmlStr;
// temp.content = htmlStr; // don't use for setting! has same bug as default `createContextualFragment`, but fine for retrieval
let frag = temp.content;
Interesting thing about this one was that if setting HTML string via temp.content
directly, then it has the same bug as the default usage of createContextualFragment()
, but setting via temp.innerHTML
produces the expected results.
html-fragments
packagelet frag = HtmlFragment(htmlStr);
This seems to be a library someone created for this exact problem (see author's comment here), likely due to the need to support browsers that don't directly support <template>
I suspect. Seems to work fine, but a bit overkill for me (to pull in a separate package just for this that is).
createContextualFragment
w/ wrapped templatelet tempFrag = document.createRange()
.createContextualFragment(`<template>${htmlStr}</template>`);
let frag = tempFrag.firstChild.content;
Kinda surprised no one found this one (so perhaps there are some limitations to it), but per my testing if you wrap the html string within a <template>
tag, then use createContextualFragment()
, then the browser seems to process the <td>
element just fine. It's really no different that Option #1, and therefore still dependent on <template>
, but I kinda prefer this option. However, if you're browser still doesn't support templates (IE), then neither option will really work reliably.
Here's a code snippet showing the issue and comparing the relevant options:
let htmlStrings = [
'<table></table>',
'<tr></tr>',
'<td></td>',
'<table><tr><td></td></tr></table>'
]
for (let htmlStr of htmlStrings) {
// default solution
let frag = document.createRange().createContextualFragment(htmlStr);
let defaultResults = fragmentToHTML(frag);
// Option 1: use <tempalte> directly
let tmp = document.createElement("template");
tmp.innerHTML = htmlStr;
// tmp.content = htmlStr; // don't use for setting! has same bug as default `createContextualFragment`, but fine for retrieval
frag = tmp.content;
let tempResults = fragmentToHTML(frag);
// Option 2: html-fragment package option
frag = HtmlFragment(htmlStr);
let hfResults = fragmentToHTML(frag);
// Option 3: wrapped <template> option
let tempFrag = document.createRange().createContextualFragment(`<template>${htmlStr}</template>`);
frag = tempFrag.firstChild.content
let wrappedResults = fragmentToHTML(frag);
console.log(htmlStr);
console.log("\t0-createContextualFragment():\t\t\t", defaultResults);
console.log("\t1-createElement('template'):\t\t\t", tempResults);
console.log("\t2-html-fragment:\t\t\t\t", hfResults);
console.log("\t3-createContextualFragment() w/ wrapped template:", wrappedResults);
}
function fragmentToHTML(frag) {
let div = document.createElement("div");
div.appendChild(frag);
return div.innerHTML;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/html-fragment.min.js"></script>
->