TypeScriptにおけるShadow DOM

TypeScriptにおけるShadow DOM

この記事ではTypeScriptにおけるShadow DOMについて説明します。

Shadow DOMの基本から、実際の使用方法までを丁寧に解説し、実践的なサンプルコードを提供します。

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>

Shadow DOM の詳細解説と実践的なステップバイステップガイド

Shadow DOM は、Web Componentsの重要な構成要素の一つであり、カプセル化されたDOMツリーを作成して、コンポーネントのスタイルや構造を外部から分離します。ここでは、Shadow DOM の基本から、実際の使用方法までを丁寧に解説し、実践的なサンプルコードを提供します。

Shadow DOM とは?

Shadow DOM は、Web標準の技術であり、以下のような特性を持っています。

  1. カプセル化

    Shadow DOM は、コンポーネント内部のDOM構造を外部から分離します。他のスタイルやスクリプトが影響を与えないため、再利用性が向上します。

  2. 独自のスタイルスコープ

    Shadow DOM 内のスタイルは、外部のCSSに影響を与えません。また、外部のスタイルもShadow DOM 内には適用されません。

  3. 独立したDOMツリー

    Shadow DOM は通常のDOMとは別のツリーとして存在し、親DOMからアクセスが制限されます。

Shadow DOM の基本的な使用方法

次のコードは、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`;
  • ブラウザには、「This is inside Shadow DOM!」という青色のテキストが表示されます。このテキストのスタイルは外部のCSSからは影響を受けません。

Shadow DOM の基本的な手順

このコードのように、Shadow DOM を使用するには、JavaScriptでattachShadowメソッドを使います。以下は、基本的な手順です。

  1. カスタム要素の作成 カスタム要素とは、HTML標準タグに加えて自分で定義できる独自のタグのことです。このステップでは、MyElement クラスのように、HTMLElement を継承したクラスを作成し、ブラウザに新しいタグとして認識させる準備を行います。作成したクラスをcustomElements.define() を使って登録することで、HTML内で独自タグとして使用可能になります。

  2. Shadow DOM のアタッチ カスタム要素内で this.attachShadow() を実行することで、Shadow DOM を作成できます。

  3. Shadow DOM 内にHTMLやCSSを追加 Shadow DOM 内に、独自の HTML 構造やスタイルを定義できます。例えば、innerHTMLにHTMLやCSSを設定することで、外部の CSS や HTML の影響を受けずに独立した見た目や挙動を持たせることが可能です。これにより、カプセル化されたコンポーネントを作ることができます。

Shadow DOM のモード: openclosed

Shadow DOM にはopenclosedの2つのモードがあります。

  • openモード: Shadow DOM にアタッチされたshadowRootに外部からアクセスできます。
  • closedモード: Shadow DOM にアタッチされたshadowRootは外部からアクセスできません。

以下は、両モードの違いを示すコード例です。

 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が出力される
  • closedモードを選択すると、shadowRootプロパティにはアクセスできなくなります。

Shadow DOM を活用したスタイルのカプセル化

Shadow DOM を使用することで、コンポーネント内のスタイルを完全にカプセル化できます。

次の例では、グローバルなスタイルと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`;
  • Shadow DOM 内のpはグローバルスタイルの影響を受けず、独自のスタイルが適用されます。

Shadow DOM の実践例: カスタムツールチップ

次に、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`;
  • このコードは、カスタムツールチップを作成し、ホバー時にスタイル付きのツールチップを表示します。

まとめ

Shadow DOM は、Web Componentsの重要な技術であり、カプセル化されたDOMとスタイルスコープを提供します。ここでは、Shadow DOM の基本概念、使用方法、モードの違い、スタイルのカプセル化、実践例について解説しました。これらを活用すると、再利用可能で堅牢なコンポーネントを作成できます。

YouTubeチャンネルでは、Visual Studio Codeを用いて上記の記事を見ながら確認できます。 ぜひYouTubeチャンネルもご覧ください。

YouTube Video