Shadow DOM in TypeScript

Shadow DOM in TypeScript

This article explains Shadow DOM in TypeScript.

We will carefully explain everything from the basics of Shadow DOM to its practical usage, and provide hands-on sample code.

YouTube Video

typescript-html-shadow-dom.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>TypeScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    h1, h2 {
 32        font-size: 1.2rem;
 33        color: #007bff;
 34        margin-top: 0.5em;
 35        margin-bottom: 0.5em;
 36        border-left: 5px solid #007bff;
 37        padding-left: 0.6em;
 38        background-color: #e9f2ff;
 39    }
 40
 41    button {
 42        display: block;
 43        margin: 1em auto;
 44        padding: 0.75em 1.5em;
 45        font-size: 1rem;
 46        background-color: #007bff;
 47        color: white;
 48        border: none;
 49        border-radius: 6px;
 50        cursor: pointer;
 51        transition: background-color 0.3s ease;
 52    }
 53
 54    button:hover {
 55        background-color: #0056b3;
 56    }
 57
 58    #output {
 59        margin-top: 1em;
 60        background-color: #1e1e1e;
 61        color: #0f0;
 62        padding: 1em;
 63        border-radius: 8px;
 64        min-height: 200px;
 65        font-family: Consolas, monospace;
 66        font-size: 0.95rem;
 67        overflow-y: auto;
 68        white-space: pre-wrap;
 69    }
 70
 71    .highlight {
 72        outline: 3px solid #ffc107; /* yellow border */
 73        background-color: #fff8e1;  /* soft yellow background */
 74        transition: background-color 0.3s ease, outline 0.3s ease;
 75    }
 76
 77    .active {
 78        background-color: #28a745; /* green background */
 79        color: #fff;
 80        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 81        transition: background-color 0.3s ease, box-shadow 0.3s ease;
 82    }
 83  </style>
 84</head>
 85<body>
 86    <div class="container">
 87        <h2>HTML Sample</h2>
 88        <div id="content"></div>
 89    </div>
 90
 91    <div class="container">
 92        <h1>JavaScript Console</h1>
 93        <button id="executeBtn">Execute</button>
 94        <div id="output"></div>
 95    </div>
 96
 97    <script>
 98        // Override console.log to display messages in the #output element
 99        (function () {
100            const originalLog = console.log;
101            console.log = function (...args) {
102                originalLog.apply(console, args);
103                const output = document.getElementById('output');
104                output.textContent += args.map(String).join(' ') + '\n';
105            };
106        })();
107
108        document.getElementById('executeBtn').addEventListener('click', () => {
109            // Prevent multiple loads
110            if (document.getElementById('externalScript')) return;
111
112            const script = document.createElement('script');
113            script.src = '/out/main.js';
114            script.id = 'externalScript';
115            //script.onload = () => console.log('typescript-html-shadow-dom.js loaded and executed.');
116            //script.onerror = () => console.log('Failed to load typescript-html-shadow-dom.js.');
117            document.body.appendChild(script);
118        });
119    </script>
120</body>
121</html>

Detailed Explanation and Practical Step-by-Step Guide to Shadow DOM

Shadow DOM is one of the key components of Web Components. It creates an encapsulated DOM tree that separates the component's style and structure from the outside. Here, we will provide a detailed explanation of the basics of Shadow DOM to its practical use cases, along with hands-on sample code.

What is Shadow DOM?

Shadow DOM is a web standard technology with the following characteristics.

  1. Encapsulation

    Shadow DOM separates the internal DOM structure of a component from the outside. Other styles and scripts do not interfere, improving reusability.

  2. Independent Style Scope

    Styles inside the Shadow DOM do not affect external CSS. Similarly, external styles do not apply within the Shadow DOM.

  3. Isolated DOM Tree

    Shadow DOM exists as a separate tree from the regular DOM, with restricted access from the parent DOM.

Basic Usage of Shadow DOM

The following code is the first example using Shadow DOM.

 1class MyElement extends HTMLElement {
 2  constructor() {
 3    super();
 4
 5    // Attach Shadow DOM
 6    const shadowRoot = this.attachShadow({ mode: 'open' });
 7
 8    // Add HTML and CSS inside Shadow DOM
 9    shadowRoot.innerHTML = `
10      <style>
11        p {
12          color: blue;
13          font-size: 18px;
14        }
15      </style>
16      <p>This is inside Shadow DOM!</p>
17    `;
18  }
19}
20
21// Register the custom element
22customElements.define('my-element', MyElement);
23
24document.getElementById('content').innerHTML = `
25  <my-element></my-element>
26`;
  • The browser will display the blue text: 'This is inside Shadow DOM!'. The style of this text is not affected by external CSS.

Basic steps of Shadow DOM

To use Shadow DOM, as shown in this code, you use the attachShadow method in JavaScript. Below are the basic steps:.

  1. Creating Custom Elements A custom element is a user-defined tag that you can create in addition to the standard HTML tags. In this step, you create a class that extends HTMLElement, like the MyElement class, preparing the browser to recognize it as a new tag. By registering the created class with customElements.define(), you can use it as a custom tag within HTML.

  2. Attaching Shadow DOM By executing this.attachShadow() inside a custom element, you can create a Shadow DOM.

  3. Adding HTML and CSS Inside the Shadow DOM Within the Shadow DOM, you can define your own HTML structure and styles. For example, by setting HTML and CSS to innerHTML, you can give it an independent look and behavior without being affected by external CSS or HTML. This allows you to create encapsulated components.

Modes of Shadow DOM: open and closed

Shadow DOM has two modes: open and closed.

  • Open mode: The shadowRoot attached to the Shadow DOM can be accessed externally.
  • Closed mode: The shadowRoot attached to the Shadow DOM cannot be accessed externally.

Below is a code example that shows the differences between the two modes.

 1class OpenElement extends HTMLElement {
 2  constructor() {
 3    super();
 4    this.attachShadow({ mode: 'open' }).innerHTML = `
 5      <p>Open Shadow DOM</p>
 6    `;
 7  }
 8}
 9
10class ClosedElement extends HTMLElement {
11  constructor() {
12    super();
13    this.attachShadow({ mode: 'closed' }).innerHTML = `
14      <p>Closed Shadow DOM</p>
15    `;
16  }
17}
18
19customElements.define('open-element', OpenElement);
20customElements.define('closed-element', ClosedElement);
21
22document.getElementById('content').innerHTML = `
23  <open-element></open-element>
24  <closed-element></closed-element>
25`;
26
27const openElement = document.querySelector('open-element') as OpenElement;
28console.log(openElement.shadowRoot); // ShadowRootが出力される
29
30const closedElement = document.querySelector('closed-element') as ClosedElement;
31console.log(closedElement.shadowRoot); // nullが出力される
  • Choosing the closed mode makes the shadowRoot property inaccessible.

Style encapsulation using Shadow DOM

Using Shadow DOM, you can completely encapsulate styles within your components.

The following example demonstrates the separation of global styles and styles within the Shadow DOM.

 1class StyledElement extends HTMLElement {
 2  constructor() {
 3    super();
 4    const shadowRoot = this.attachShadow({ mode: 'open' });
 5
 6    shadowRoot.innerHTML = `
 7      <style>
 8        p {
 9          background-color: lightblue;
10          padding: 10px;
11          border: 1px solid blue;
12        }
13      </style>
14      <p>Shadow DOM Styled Content</p>
15    `;
16  }
17}
18
19customElements.define('styled-element', StyledElement);
20
21document.getElementById('content').innerHTML = `
22  <style>
23    p {
24      color: red;
25      font-weight: bold;
26    }
27  </style>
28
29  <styled-element></styled-element>
30`;
  • p elements inside the Shadow DOM are not affected by global styles and have their own unique styles applied.

Practical example of Shadow DOM: Custom Tooltip

Next, we introduce an example of creating a custom tooltip using Shadow DOM.

 1class Tooltip extends HTMLElement {
 2  constructor() {
 3    super();
 4
 5    const shadowRoot = this.attachShadow({ mode: 'open' });
 6
 7    shadowRoot.innerHTML = `
 8      <style>
 9        :host {
10          position: relative;
11          display: inline-block;
12          cursor: pointer;
13        }
14
15        .tooltip {
16          visibility: hidden;
17          background-color: black;
18          color: white;
19          text-align: center;
20          padding: 5px;
21          border-radius: 5px;
22          position: absolute;
23          bottom: 125%;
24          left: 50%;
25          transform: translateX(-50%);
26          white-space: nowrap;
27        }
28
29        :host(:hover) .tooltip {
30          visibility: visible;
31        }
32      </style>
33      <slot></slot>
34      <div class="tooltip">Tooltip text</div>
35    `;
36  }
37}
38
39customElements.define('custom-tooltip', Tooltip);
40
41document.getElementById('content').innerHTML = `
42  <custom-tooltip>
43    Hover over me
44    <span slot="tooltip">This is a custom tooltip!</span>
45  </custom-tooltip>
46`;
  • This code creates a custom tooltip and displays a styled tooltip on hover.

Summary

Shadow DOM is a crucial technology for Web Components, providing an encapsulated DOM and style scope. Here, we covered the basics of Shadow DOM, its usage, mode differences, style encapsulation, and practical examples. By leveraging these, you can build reusable and robust components.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video