Skip to content

Instantly share code, notes, and snippets.

@jakelazaroff
Last active December 4, 2023 10:16
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakelazaroff/e28c44c860656d736c80d16f3f1a638c to your computer and use it in GitHub Desktop.
Save jakelazaroff/e28c44c860656d736c80d16f3f1a638c to your computer and use it in GitHub Desktop.
simple web component that sandboxes its slotted elements inside an iframe
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;
}
}
);
<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