Shadow DOM trong JavaScript
Bài viết này giải thích về Shadow DOM trong JavaScript.
YouTube Video
javascript-html-shadow-dom.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & 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 id="shadow-host">Shadow Root Element</div>
90 <my-card></my-card>
91 </div>
92
93 <div class="container">
94 <h1>JavaScript Console</h1>
95 <button id="executeBtn">Execute</button>
96 <div id="output"></div>
97 </div>
98
99 <div>
100 <h2>Slot Sample</h2>
101 <my-element>
102 <h3 slot="header">Header Content</h1>
103 <p slot="content">Main Content</p>
104 </my-element>
105 </div>
106
107 <script>
108 // Override console.log to display messages in the #output element
109 (function () {
110 const originalLog = console.log;
111 console.log = function (...args) {
112 originalLog.apply(console, args);
113 const output = document.getElementById('output');
114 output.textContent += args.map(String).join(' ') + '\n';
115 };
116 })();
117
118 document.getElementById('executeBtn').addEventListener('click', () => {
119 // Prevent multiple loads
120 if (document.getElementById('externalScript')) return;
121
122 const script = document.createElement('script');
123 script.src = 'javascript-html-shadow-dom.js';
124 script.id = 'externalScript';
125 //script.onload = () => console.log('javascript-html-shadow-dom.js loaded and executed.');
126 //script.onerror = () => console.log('Failed to load javascript-html-shadow-dom.js.');
127 document.body.appendChild(script);
128 });
129 </script>
130</body>
131</html>
Hiểu về Shadow DOM
Shadow DOM
là một tính năng mạnh mẽ của chuẩn Web Components, cho phép đóng gói các kiểu dáng (styles) và cấu trúc DOM trong các thành phần. Tính năng này ngăn chặn sự xung đột giữa các kiểu dáng và script của các thành phần và tài liệu chính.
Shadow DOM
trong JavaScript
Shadow DOM
cung cấp một cách tạo cây DOM phân vùng liên kết với một phần tử DOM thông thường. Cây Shadow này được cách ly khỏi tài liệu tổng thể, nơi các kiểu dáng và script bên ngoài không tác động đến nó, cũng như các kiểu dáng và script bên trong không bị rò rỉ ra ngoài.
Ví dụ, nếu bạn tạo một thành phần nút bấm tùy chỉnh sử dụng Shadow DOM
, các kiểu dáng của nó sẽ không ảnh hưởng đến các phần tử khác trên trang web. Tương tự, các phần tử có cùng tên class sẽ không bị xung đột.
Nội dung HTML thông thường bên ngoài Shadow DOM
được gọi là Light DOM
.
Lợi ích của Shadow DOM
-
Đóng gói (Encapsulation)
Shadow DOM
tách biệt kiểu dáng và chức năng, ngăn ngừa xung đột với các style và script toàn cục.
-
Tái sử dụng (Reusability)
- Các thành phần được xây dựng với
Shadow DOM
có thể được tái sử dụng ở nhiều dự án khác nhau mà không lo về xung đột kiểu dáng.
- Các thành phần được xây dựng với
-
Dễ bảo trì (Maintainability)
- Đóng gói giúp logic và kiểu dáng của thành phần được tách biệt, giúp dễ dàng gỡ lỗi và bảo trì hơn.
Tạo Shadow DOM
Để sử dụng Shadow DOM
, bạn cần gắn một shadow root vào một phần tử HTML. Dưới đây là một ví dụ đơn giản:.
1// Select the host element
2const hostElement = document.querySelector('#shadow-host');
3
4// Attach a shadow root
5const shadowRoot = hostElement.attachShadow({ mode: 'open' });
Giải thích
Đoạn mã này bao gồm các thành phần sau:.
-
Thành phần chủ (Host Element)
- Một phần tử DOM thông thường mà shadow root được gắn vào (trong trường hợp này là
#shadow-host
).
- Một phần tử DOM thông thường mà shadow root được gắn vào (trong trường hợp này là
-
Shadow Root
- Gốc của cây shadow được tạo ra bằng cách sử dụng
attachShadow
.
- Gốc của cây shadow được tạo ra bằng cách sử dụng
-
Chế độ (Mode)
- Ở chế độ
open
, JavaScript bên ngoài có thể truy cập vào shadow root thông quaelement.shadowRoot
. Ngược lại, chế độclosed
không cho phép truy cập.
- Ở chế độ
Tạo kiểu trong Shadow DOM
Shadow DOM
có phạm vi kiểu dáng riêng của nó. Các kiểu dáng được định nghĩa bên trong cây bóng chỉ áp dụng cho các phần tử bên trong cây đó. Dưới đây là một ví dụ:.
1// Add content to the shadow root
2shadowRoot.innerHTML = `
3 <style>
4 p {
5 color: green;
6 }
7 </style>
8 <p>Scoped style inside Shadow DOM.</p>
9`;
Ngay cả khi có các kiểu dáng xung đột trong tài liệu chính, chúng cũng không ảnh hưởng đến đoạn văn bên trong cây shadow.
1const content = document.getElementById('content');
2content.innerHTML = `
3 <style>
4 p {
5 color: red;
6 }
7 </style>
8 <p>This is in the main DOM.</p>
9`;
- Đoạn văn bên trong
Shadow DOM
vẫn giữ màu xanh lá, trong khi đoạn bên ngoài có màu đỏ.
Các sự kiện bên trong Shadow DOM
Các sự kiện bên trong Shadow DOM
tương tự như các sự kiện DOM thông thường nhưng có thể hoạt động khác biệt về mặt lan truyền do tính đóng gói.
Dưới đây là một ví dụ:.
1// Add an event listener inside Shadow DOM
2shadowRoot.innerHTML = `
3 <div id="button-container">
4 <button id="shadow-button">Click Me</button>
5 </div>
6`;
7
8shadowRoot.querySelector('#shadow-button').addEventListener('click', (event) => {
9 console.log('Button : Button clicked inside Shadow DOM');
10 console.log(event.target);
11});
12
13shadowRoot.querySelector('#button-container').addEventListener('click', (event) => {
14 console.log('Container : Button clicked inside Shadow DOM');
15 console.log(event.target);
16});
17
18hostElement.addEventListener('click', (event) => {
19 console.log('Event bubbled to the host element');
20 console.log(event.target);
21});
22
23document.addEventListener('click', (event) => {
24 console.log('Document listener');
25 console.log(event.target);
26});
- Khi nút được nhấn, cả hai trình nghe đều được kích hoạt, minh họa hành vi của sự kiện bong bóng.
- Khi một sự kiện phát sinh bên trong
Shadow DOM
lan lênLight DOM
, thuộc tínhtarget
của sự kiện sẽ được thay đổi thành phần tử chủ thay vì nguồn gốc ban đầu.- Trong ví dụ này,
event.target
bên trongShadow DOM
là phần tửbutton
thực tế, nhưng bên ngoàiShadow DOM
, nó được thay thế bằng phần tử chủdiv
.
- Trong ví dụ này,
Shadow DOM
và các phần tử tùy chỉnh
Shadow DOM
và các phần tử tùy chỉnh là thành phần chính của Web Components
. Chúng là các công nghệ được sử dụng để tạo ra các thành phần giao diện người dùng có thể tái sử dụng và đóng gói.
1class MyCard extends HTMLElement {
2 constructor() {
3 super();
4 const shadow = this.attachShadow({ mode: 'open' });
5 shadow.innerHTML = `
6 <style>
7 p {
8 color: blue;
9 }
10 </style>
11 <p>I'm inside shadow DOM</p>
12 `;
13 }
14}
15
16customElements.define('my-card', MyCard);
Tài liệu chính chứa HTML như sau:.
1<my-card></my-card>
- Bằng cách sử dụng
Shadow DOM
bên trong các phần tử tùy chỉnh, bạn có thể xây dựng các thành phần có thể tái sử dụng và chống xung đột kiểu dáng. Trong đoạn mã này, một thẻ có tênmy-card
được tạo ra và liên kết với lớpMyCard
. Phần tử<p>
bên trongmy-card
không bị ảnh hưởng bởi các kiểu dáng bên ngoài và luôn được hiển thị màu xanh dương.
Slots: Phân phối nội dung Light DOM
Slots cho phép bạn chèn nội dung Light DOM
vào bên trong Shadow DOM
. Dưới đây là một ví dụ:.
1class MyElement extends HTMLElement {
2 constructor() {
3 super();
4 const shadow = this.attachShadow({ mode: 'open' });
5
6 shadow.innerHTML = `
7 <style>
8 .container {
9 border: 2px solid #ccc;
10 padding: 16px;
11 border-radius: 8px;
12 font-family: sans-serif;
13 }
14 .header {
15 font-size: 1.2em;
16 color: darkblue;
17 margin-bottom: 8px;
18 }
19 .content {
20 font-size: 1em;
21 color: #333;
22 }
23 </style>
24 <div class="container">
25 <div class="header">
26 <slot name="header"></slot>
27 </div>
28 <div class="content">
29 <slot name="content"></slot>
30 </div>
31 </div>
32 `;
33 }
34}
35
36customElements.define('my-element', MyElement);
Tài liệu chính chứa HTML như sau:.
1<my-element>
2 <h3 slot="header">Header Content</h1>
3 <p slot="content">Main Content</p>
4</my-element>
- Phần tử
slot
bên trongShadow DOM
sẽ hiển thị nội dungLight DOM
với thuộc tínhslot
tương ứng.
Kết luận
Shadow DOM
là một công cụ quan trọng để xây dựng các thành phần web mạnh mẽ, có thể tái sử dụng và dễ duy trì. Bằng cách đóng gói kiểu dáng và chức năng, nó giảm khả năng xung đột và đơn giản hóa việc quản lý mã nguồn.
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.