Skip to content

Instantly share code, notes, and snippets.

@dgp1130
Last active March 8, 2022 05:27
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save dgp1130/1283773eb5890fb3cf59005892231211 to your computer and use it in GitHub Desktop.
HTML Fragments
/**
* Parse a network response as an HTML document fragment, then returns each
* top-level element.
*/
export async function parseDomFragment(res) {
// Parse a fully rendered document fragment from the network response.
const html = await res.text();
const contentType = res.headers.get('Content-Type');
if (!contentType)
throw new Error('Response has no Content-Type.');
const simpleContentType = contentType.indexOf(';') === -1
? contentType
: contentType.slice(0, contentType.indexOf(';'));
// Parse the HTML, extract the top-level nodes, adopt them into the current
// document, and fix the `<script />` tags.
const parse = DOMParser.prototype.parseFromString;
const fragment = parse.apply(new DOMParser(), [
html,
simpleContentType,
{ includeShadowRoots: true },
]);
const adoptedNodes = Array.from(fragment.body.children).map((fragEl) => {
const el = document.adoptNode(fragEl);
replaceScripts(el);
return el;
});
// Wrap everything in a template so it can be cloned as necessary.
const template = document.createElement('template');
template.content.append(...adoptedNodes);
return template;
}
/**
* Replace each `<script />` in the given element with a copy.
* `DOMParser.parseFromString()` disables `<script />` tags. Cloning and
* replacing each `<script />` tag means it will be loaded when attached to the
* active document.
*
* Also note that `<script />` tags should include `type="module"`, or else
* multiple DOM fragments with the same `<script src="..."></script>` will fetch
* and execute the resource multiple times on the same page. Module scripts have
* a cache so multiple tags of the same resource won't duplicate execution.
*
* @link https://www.w3.org/TR/DOM-Parsing/#:~:text=script%20elements%20get%20marked%20unexecutable%20and%20the%20contents%20of%20noscript%20get%20parsed%20as%20markup.
* @link https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
* @link https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script
*/
function replaceScripts(el) {
for (const oldScript of Array.from(el.querySelectorAll('script'))) {
const newScript = document.createElement('script');
for (const name of oldScript.getAttributeNames()) {
newScript.setAttribute(name, oldScript.getAttribute(name));
}
newScript.textContent = oldScript.textContent;
oldScript.replaceWith(newScript);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment