צללים DOM בג'אווהסקריפט
מאמר זה מסביר את ה-Shadow DOM בג'אווהסקריפט.
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>
הבנת Shadow DOM
Shadow DOM
הוא תכונה חזקה של תקן רכיבי ווב שמאפשרת איקפסולציה של סגנונות ומבנה DOM בתוך רכיבים. תכונה זו מונעת התנגשות של סגנונות וסקריפטים בין רכיבים ובין מסמך ה-HTML הראשי.
Shadow DOM
בג'אווהסקריפט
Shadow DOM
מספק דרך ליצור עץ DOM מוגדר ומקושר לאלמנט DOM רגיל. עץ הצל מבודד מהמסמך הכללי, כך שסגנונות וסקריפטים חיצוניים אינם משפיעים עליו, וסגנונות וסקריפטים פנימיים אינם דולפים החוצה.
לדוגמה, אם תיצור רכיב כפתור מותאם אישית באמצעות Shadow DOM
, הסגנונות שלו לא יפריעו לשאר האלמנטים בדף. באופן דומה, אלמנטים עם אותו שם מחלקה לא יתנגשו.
התוכן ה-HTML הרגיל שמחוץ ל-Shadow DOM
נקרא Light DOM
.
היתרונות של Shadow DOM
-
הכלה (Encapsulation)
- ה-
Shadow DOM
מפריד בין עיצוב ופונקציונליות, ומונע התנגשויות עם סגנונות וסקריפטים גלובליים.
- ה-
-
שימוש חוזר (Reusability)
- רכיבים שנבנים עם
Shadow DOM
יכולים לשמש בפרויקטים שונים מבלי לחשוש מהתנגשויות בעיצוב.
- רכיבים שנבנים עם
-
תחזוקה (Maintainability)
- ההכלה (Encapsulation) הופכת את הלוגיקה והעיצוב של הרכיב לעצמאיים, ומקלה על ניפוי שגיאות ותחזוקה.
יצירת Shadow DOM
כדי להשתמש ב- Shadow DOM
, יש לצרף שורש צל לאלמנט HTML. הנה דוגמה פשוטה:.
1// Select the host element
2const hostElement = document.querySelector('#shadow-host');
3
4// Attach a shadow root
5const shadowRoot = hostElement.attachShadow({ mode: 'open' });
הסבר
הקוד הזה מכיל את האלמנטים הבאים:.
-
אלמנט מארח (Host Element)
- אלמנט DOM רגיל שאליו מוצמד השורש של ה-Shadow (במקרה הזה,
#shadow-host
).
- אלמנט DOM רגיל שאליו מוצמד השורש של ה-Shadow (במקרה הזה,
-
שורש צל (Shadow Root)
- שורש עץ הצל שנוצר באמצעות
attachShadow
.
- שורש עץ הצל שנוצר באמצעות
-
מצב (Mode)
- במצב
open
, קוד ג'אווהסקריפט חיצוני יכול לגשת לשורש הצל דרךelement.shadowRoot
. מנגד, במצבclosed
לא מתאפשרת גישה.
- במצב
עיצוב בתוך Shadow DOM
ל-Shadow DOM
יש תחום סגנון משלו. סגנונות שהוגדרו בתוך עץ ה-shadow חלים רק על האלמנטים שבתוך העץ הזה. הנה דוגמה:.
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`;
גם אם קיימים עיצובים מתנגשים במסמך הראשי, הם לא ישפיעו על הפסקה שבתוך עץ הצל.
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`;
- הפסקה שבתוך ה-
Shadow DOM
נשארת בירוק, בעוד שהפסקה החיצונית אדומה.
אירועים בתוך ה-Shadow DOM
אירועים בתוך ה-Shadow DOM
דומים לאירועים רגילים ב-DOM, אך יכולים להתנהג בצורה שונה מבחינת התפשטות עקב הקפסולציה.
הנה דוגמה:.
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});
- כאשר לוחצים על הכפתור, שני המאזינים מופעלים, ומדגימים את ההתנהגות של התפשטות אירועים כלפי מעלה.
- כאשר אירוע שמקורו בתוך ה־
Shadow DOM
עולה (bubbles) אל תוך ה־Light DOM
, המטרה (target
) של האירוע משתנה לאלמנט המארח במקום המקור המקורי.- בדוגמה זו, ה־
event.target
בתוך ה־Shadow DOM
הוא אלמנט ה־button
עצמו, אך מחוץ ל־Shadow DOM
הוא מוחלף לאלמנט ה־div
המארח.
- בדוגמה זו, ה־
Shadow DOM
ואלמנטים מותאמים אישית (Custom Elements)
Shadow DOM
ואלמנטים מותאמים אישית הם מרכיבים מרכזיים של Web Components
. אלו טכנולוגיות המשמשות ליצירת רכיבי ממשק משתמש הניתנים לשימוש חוזר ומבודדים.
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);
המסמך הראשי מכיל HTML כזה:.
1<my-card></my-card>
- באמצעות שימוש ב־
Shadow DOM
בתוך אלמנטים מותאמים אישית, ניתן לבנות רכיבים לשימוש חוזר שחסינים לקונפליקטים של עיצובים. בקוד הזה, תג בשםmy-card
נוצר ומשויך למחלקהMyCard
. אלמנט ה־<p>
בתוךmy-card
אינו מושפע מסגנונות חיצוניים והוא תמיד מוצג בצבע כחול.
חריצים (Slots): פיזור תוכן Light DOM
החריצים (Slots) מאפשרים להקרין (project) תוכן מ-Light DOM
אל תוך ה-Shadow DOM
. הנה דוגמה:.
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);
המסמך הראשי מכיל HTML כזה:.
1<my-element>
2 <h3 slot="header">Header Content</h1>
3 <p slot="content">Main Content</p>
4</my-element>
- אלמנט ה-
slot
שבפנים ה-Shadow DOM
מציג תוכן מ-Light DOM
שיש לו את אותו מאפייןslot
.
סיום
Shadow DOM
הוא כלי חיוני לבניית רכיבים אינטרנטיים חזקים, ניתנים לשימוש חוזר וניתנים לתחזוקה. על ידי קפסולציה של סגנונות ופונקציונליות, הוא מפחית את האפשרות להתנגשויות ומפשט את ניהול בסיס הקוד.
תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.