Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.
Understanding Kristofer Joseph’s Single File Web Component#
Via Brian LeRoux I found single-file-web-component.html by Kristofer Joseph. It’s really clever! It demonstrates how to build a <hello-world></hello-world> custom Web Component in a single HTML file, using some neat tricks.
For my own education I spent some time picking it apart and built my own annotated version of the code showing what I learned.
Justin Fagnani provided useful feedback on this on Twitter.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Single File Web Component</title>6 <style>7 body {8 background-color: #eee;9 font-family: Helvetica, sans-serif;10 }11 h1 {12 color: blue;13 background-color: pink;14 }15 </style>16 </head>17 <body>18 <template id="single-file">19 <style>20 /*21 These styles affect only content inside the shadow DOM.22
23 Styles in the outside document mostly do not affect stuff24 here, but there are some exceptions: font-family affects25 this <h1> for example. I don't understand the rules here.26 */27 h1 {28 color: red;29 }30 </style>31
32 <h1>Hello world (web component)</h1>33 <!--34 This code still works if you remove the type="module" parameter.35
36 Using that parameter enables features such as 'import ... from'37
38 More importantly it stops variables inside the script tag from39 leaking out to the global scope - if you remove type="module"40 from here then the HelloWorld class becomes visible.41 -->42 <script type="module">43 class HelloWorld extends HTMLElement {44 constructor() {45 /*46 If you remove the call to super() here Firefox shows an error:47 "Uncaught ReferenceError: must call super constructor before48 using 'this' in derived class constructor'"49 */50 super();51 const template = document.getElementById("single-file");52 /*53 mode: 'open' means you are allowed to access54 document.querySelector('hello-world').shadowRoot to get55 at the DOM inside. Set to 'closed' and the .shadowRoot56 property will return null.57 */58 this.attachShadow({ mode: "open" }).appendChild(59 template.content.cloneNode(true)60 );61 /*62 template.content is a 'DocumentFragment' array.63
64 template.content.cloneNode() without the true performs65 a shallow clone, which provides a empty DocumentFragment66 array.67
68 template.content.cloneNode(true) provides one with 6 items69 */70 }71 connectedCallback() {72 // https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks73 console.log("Why hello there 👋");74 }75 }76 customElements.define("hello-world", HelloWorld);77 </script>78 </template>79
80 <h1>This is not a web component</h1>81
82 <hello-world></hello-world>83
84 <script>85 const sf = document.getElementById("single-file");86 /*87 Before executing this line, sf.content.lastElementChild88 is the <script type="module"> element hidden away inside89 the <template> - we remove it from the template here and90 append it to the document.body, causing it to execute in91 the main document and activate the <hello-world> tag.92 */93 document.body.appendChild(sf.content.lastElementChild);94 </script>95 </body>96</html>Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.