Last active
December 4, 2023 10:16
-
-
Save jakelazaroff/e28c44c860656d736c80d16f3f1a638c to your computer and use it in GitHub Desktop.
simple web component that sandboxes its slotted elements inside an iframe
This file contains hidden or 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
customElements.define( | |
"i-frame", | |
class extends HTMLElement { | |
#shadow = this.attachShadow({ mode: "closed" }); | |
constructor() { | |
super(); | |
this.#shadow.innerHTML = ` | |
<slot></slot> | |
<iframe part="frame" srcdoc=""></iframe> | |
<style> | |
slot { | |
display: none; | |
} | |
</style> | |
`; | |
this.addEventListener("slotchange", this); | |
} | |
handleEvent(evt) { | |
if (evt.type === "slotchange") this.#render(); | |
} | |
get window() { | |
return this.#shadow.querySelector("iframe").contentWindow; | |
} | |
#render() { | |
const iframe = this.#shadow.querySelector("iframe"); | |
if (this.hasAttribute("sandbox")) iframe.sandbox = this.getAttribute("sandbox"); | |
this.ready = new Promise(resolve => (iframe.onload = resolve)); | |
const contents = this.#shadow | |
.querySelector("slot") | |
.assignedElements() | |
.flatMap(el => (el instanceof HTMLSlotElement ? el.assignedElements() : el)) | |
.map(el => el.cloneNode(true)); | |
for (const el of contents) { | |
if (el.tagName === "SCRIPT") el.type = el.type.replace(/^sandbox:/, ""); | |
if (el.tagName === "STYLE") el.type = el.type.replace(/^sandbox:?/, ""); | |
} | |
const body = contents.map(el => el.outerHTML).join("\n"); | |
const lang = this.getAttribute("lang") || "en"; | |
iframe.srcdoc = ` | |
<!DOCTYPE html> | |
<html lang="${lang}"> | |
<body> | |
${body} | |
</body> | |
</html> | |
`; | |
return this.ready; | |
} | |
} | |
); |
This file contains hidden or 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
<i-frame sandbox="allow-scripts"> | |
<p>hello from inside an iframe!</p> | |
<p>sandbox attributes get passed through</p> | |
<script type="sandbox:module"> | |
// prefix script tag types with `sandbox` or `sandbox:` to prevent them from executing in the parent page | |
</script> | |
<style type="sandbox"> | |
/* same thing with style tag types */ | |
</style> | |
</i-frame> | |
<script> | |
// access the iframe's window via the `window` property | |
document.querySelector("i-frame").window | |
</script> | |
<style> | |
/* style the iframe with the `frame` part */ | |
i-frame::part(frame) { | |
border: 1px solid black; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment