Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save exside/dc072926e8ac6062b98690e5e84d1abe to your computer and use it in GitHub Desktop.
Save exside/dc072926e8ac6062b98690e5e84d1abe to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Single File Web Component</title>
<style>
body {
background-color: #eee;
font-family: Helvetica, sans-serif;
}
h1 {
color: blue;
background-color: pink;
}
</style>
</head>
<body>
<template id="single-file">
<style>
/*
These styles affect only content inside the shadow DOM.
Styles in the outside document mostly do not affect these,
with the exception of inheritable styles such as color,
font and line-height.
*/
h1 {
color: red;
}
</style>
<h1>Hello world (web component)</h1>
<!--
This code still works if you remove the type="module" parameter.
Using that parameter enables features such as 'import ... from'
More importantly it stops variables inside the script tag from
leaking out to the global scope - if you remove type="module"
from here then the HelloWorld class becomes visible.
-->
<script type="module">
class HelloWorld extends HTMLElement {
constructor() {
/*
If you remove the call to super() here Firefox shows an error:
"Uncaught ReferenceError: must call super constructor before
using 'this' in derived class constructor'"
*/
super();
const template = document.getElementById("single-file");
/*
mode: 'open' means you are allowed to access
document.querySelector('hello-world').shadowRoot to get
at the DOM inside. Set to 'closed' and the .shadowRoot
property will return null.
*/
this.attachShadow({ mode: "open" }).appendChild(
template.content.cloneNode(true)
);
/*
template.content is a 'DocumentFragment' array.
template.content.cloneNode() without the true performs
a shallow clone, which provides a empty DocumentFragment
array.
template.content.cloneNode(true) provides one with 6 items
*/
}
connectedCallback() {
// https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks
console.log("Why hello there 👋");
}
}
customElements.define("hello-world", HelloWorld);
</script>
</template>
<h1>This is not a web component</h1>
<hello-world></hello-world>
<script>
const sf = document.getElementById("single-file");
/*
Before executing this line, sf.content.lastElementChild
is the <script type="module"> element hidden away inside
the <template> - we remove it from the template here and
append it to the document.body, causing it to execute in
the main document and activate the <hello-world> tag.
*/
document.body.appendChild(sf.content.lastElementChild);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment