Skip to content

Instantly share code, notes, and snippets.

@myobie
Last active September 4, 2023 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myobie/f39ce17346921850af51f2ca26edf6f3 to your computer and use it in GitHub Desktop.
Save myobie/f39ce17346921850af51f2ca26edf6f3 to your computer and use it in GitHub Desktop.
Dreaming of HTML imports 🤔
// Example usage:
// <script type="module" src="./html-import.js"></script>
// <html-import href="./example.html" runscripts></html-import>
// <!-- now we can use the component defined from example.html -->
// <example-custom-element><p>This paragraph will be slotted.</p></example-custom-element>
export class HTMLImport extends HTMLElement {
static {
customElements.define('html-import', this)
}
static get observedAttributes() {
return ['href']
}
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
attributeChangedCallback(name, _oldValue, newValue) {
if (name !== 'href') { return }
this.#doFetch(newValue)
}
#clear() {
while(this.firstChild) this.removeChild(this.lastChild)
while(this.shadowRootfirstChild) this.removeChild(this.shadowRoot.lastChild)
}
async #doFetch(href) {
if (!href) {
this.#clear()
return
}
const parser = new DOMParser()
const response = await fetch(href)
const text = await response.text()
const doc = parser.parseFromString(text, 'text/html')
const template = document.createElement('template')
template.innerHTML = doc.body.innerHTML
const hrefNow = this.getAttribute('href')
if (hrefNow === href) {
this.#clear()
this.shadowRoot.append(template)
this.append(template.content.cloneNode(true))
const runScripts = this.getAttribute('runscripts') !== null
if (runScripts) {
for (const el of doc.body.children) {
if (el.tagName === 'SCRIPT') {
const script = document.createElement('script')
script.innerHTML = el.textContent
if (el.src) { script.src = el.src }
if (el.type) { script.type = el.type }
this.append(script)
}
}
}
}
}
}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>This is an HTML page!</title>
</head>
<body>
<template data-name="example-custom-element">
<style>
p, ::slotted(p) {
font-style: italic;
}
</style>
<p>This is a paragraph from the template in example.html</p>
<slot></slot>
</template>
<script>
customElements.define('example-custom-element', class extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
connectedCallback() {
const template = document.querySelector('template[data-name="example-custom-element"]')
this.shadowRoot.append(template.content.cloneNode(true))
const p = document.createElement('p')
p.innerText = 'This is a paragraph from the connectedCallback() in example.html to prove that scripts run'
this.shadowRoot.append(p)
}
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment