Last active
March 8, 2022 05:27
-
-
Save dgp1130/1283773eb5890fb3cf59005892231211 to your computer and use it in GitHub Desktop.
HTML Fragments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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