Shadow DOM trong TypeScript

Shadow DOM trong TypeScript

Bài viết này giải thích về Shadow DOM trong TypeScript.

Chúng tôi sẽ giải thích cẩn thận mọi thứ từ kiến thức cơ bản về Shadow DOM đến cách sử dụng thực tế, đồng thời cung cấp mã mẫu thực hành.

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>

Giải thích chi tiết và hướng dẫn từng bước thực tiễn về Shadow DOM

Shadow DOM là một trong những thành phần chính của Web Components. Nó tạo ra một cây DOM được đóng gói, tách biệt kiểu dáng và cấu trúc của thành phần khỏi bên ngoài. Ở đây, chúng tôi sẽ cung cấp một giải thích chi tiết từ các khái niệm cơ bản của Shadow DOM đến các trường hợp sử dụng thực tiễn, kèm theo mã mẫu thực hành.

Shadow DOM là gì?

Shadow DOM là một công nghệ chuẩn web với các đặc điểm sau.

  1. Đóng gói

    Shadow DOM tách biệt cấu trúc DOM nội bộ của một thành phần khỏi bên ngoài. Các kiểu dáng và script khác không can thiệp, nâng cao khả năng tái sử dụng.

  2. Phạm vi kiểu dáng độc lập

    Các kiểu dáng bên trong Shadow DOM không ảnh hưởng đến CSS bên ngoài. Tương tự, các kiểu dáng bên ngoài không áp dụng bên trong Shadow DOM.

  3. Cây DOM độc lập

    Shadow DOM tồn tại như một cây tách biệt khỏi DOM thông thường, với quyền truy cập bị giới hạn từ DOM cha.

Cách sử dụng cơ bản của Shadow DOM

Đoạn mã sau là ví dụ đầu tiên sử dụng 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`;
  • Trình duyệt sẽ hiển thị văn bản màu xanh: 'Đây là bên trong Shadow DOM!'. Kiểu của văn bản này không bị ảnh hưởng bởi CSS bên ngoài.

Các bước cơ bản của Shadow DOM

Để sử dụng Shadow DOM, như minh họa trong đoạn mã này, bạn sử dụng phương thức attachShadow trong JavaScript. Dưới đây là các bước cơ bản:.

  1. Tạo các phần tử tùy chỉnh Phần tử tùy chỉnh là thẻ do người dùng định nghĩa mà bạn có thể tạo thêm bên cạnh các thẻ HTML tiêu chuẩn. Ở bước này, bạn tạo một lớp kế thừa từ HTMLElement, ví dụ như lớp MyElement, để trình duyệt có thể nhận diện nó như một thẻ mới. Bằng cách đăng ký lớp vừa tạo với customElements.define(), bạn có thể sử dụng nó như một thẻ tùy chỉnh trong HTML.

  2. Gắn thêm Shadow DOM Bằng cách thực thi this.attachShadow() bên trong một phần tử tùy chỉnh, bạn có thể tạo một Shadow DOM.

  3. Thêm HTML và CSS vào bên trong Shadow DOM Bên trong Shadow DOM, bạn có thể định nghĩa cấu trúc HTML và các kiểu CSS của riêng mình. Ví dụ, bằng cách gán HTML và CSS vào innerHTML, bạn có thể tạo giao diện và hành vi độc lập mà không bị ảnh hưởng bởi CSS hoặc HTML bên ngoài. Điều này cho phép bạn tạo ra các thành phần được đóng gói.

Chế độ của Shadow DOM: mởđóng

Shadow DOM có hai chế độ: mởđóng.

  • Chế độ mở: shadowRoot gắn với Shadow DOM có thể được truy cập từ bên ngoài.
  • Chế độ đóng: shadowRoot gắn với Shadow DOM không thể được truy cập từ bên ngoài.

Dưới đây là một ví dụ mã cho thấy sự khác biệt giữa hai chế độ.

 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が出力される
  • Chọn chế độ đóng sẽ làm thuộc tính shadowRoot không thể truy cập.

Bao bọc kiểu dáng bằng cách sử dụng Shadow DOM

Bằng cách sử dụng Shadow DOM, bạn có thể hoàn toàn đóng gói kiểu dáng bên trong các thành phần của mình.

Ví dụ sau minh họa sự tách biệt giữa các kiểu dáng toàn cầu và các kiểu dáng trong 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 bên trong Shadow DOM không bị ảnh hưởng bởi các kiểu dáng toàn cục và có các kiểu dáng riêng biệt của chúng được áp dụng`.

Ví dụ thực tế của Shadow DOM: Tooltip tùy chỉnh

Tiếp theo, chúng tôi giới thiệu một ví dụ về việc tạo tooltip tùy chỉnh bằng 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`;
  • Đoạn mã này tạo một tooltip tùy chỉnh và hiển thị tooltip có kiểu dáng khi di chuột.

Tóm tắt

Shadow DOM là một công nghệ quan trọng đối với Web Components, cung cấp một DOM và phạm vi kiểu dáng được bao bọc. Ở đây, chúng tôi đã đề cập đến những điều cơ bản về Shadow DOM, cách sử dụng, sự khác biệt trong chế độ, bao gói kiểu dáng, và các ví dụ thực tế. Bằng cách tận dụng những điều này, bạn có thể xây dựng các thành phần có thể tái sử dụng và mạnh mẽ.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video