79748410

Date: 2025-08-27 19:41:17
Score: 0.5
Natty:
Report link

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>'

Option 1 - Create <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.

Option 2 - Use html-fragments package

let 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).

Option 3 - createContextualFragment w/ wrapped template

let 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>

->

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @dandavis'using
  • Low reputation (1):
Posted by: gerneio