سجلدوم في جافا سكريبت

سجلدوم في جافا سكريبت

توضح هذه المقالة سجلدوم في جافا سكريبت۔

YouTube Video

javascript-html-shadow-dom.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &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 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 هي ميزة قوية في معيار Web Components تتيح تغليف الأنماط وبنية DOM داخل المكونات۔ تمنع هذه الميزة تداخل الأنماط والسكربتات بين المكونات والمستند الرئيسي۔

Shadow DOM في جافا سكريبت

Shadow DOM يوفر طريقة لإنشاء شجرة DOM محدودة ترتبط بعنصر DOM عادي۔ تكون هذه الشجرة الظلية معزولة عن المستند الكلي، حيث لا تؤثر عليها الأنماط والسكربتات الخارجية، ولا تتسرب الأنماط والسكربتات الداخلية منها۔

على سبيل المثال، إذا قمت بإنشاء مكون زر مخصص باستخدام Shadow DOM، فإن أنماطه لن تتداخل مع العناصر الأخرى على الصفحة۔ وبالمثل، لن تتعارض العناصر التي تحمل نفس اسم الفئة۔

يشار إلى محتوى HTML العادي خارج Shadow DOM باسم Light DOM۔

فوائد Shadow DOM

  1. الكبسلة (Encapsulation)

    • Shadow DOM يفصل بين التنسيق (الأنماط) والوظائف، مما يمنع التعارض مع الأنماط والنصوص البرمجية العامة۔
  2. إعادة الاستخدام

    • يمكن إعادة استخدام المكونات المبنية باستخدام Shadow DOM في مشاريع مختلفة بدون القلق من تعارض الأنماط۔
  3. سهولة الصيانة

    • تجعل الكبسلة (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' });

الشرح

يتضمن هذا الكود العناصر التالية:۔

  1. عنصر الاستضافة (Host Element)

    • عنصر DOM عادي يتم إرفاق جذر الظل (shadow root) به (في هذه الحالة، #shadow-host
  2. جذر الظل (Shadow Root)

    • هو جذر شجرة الظل التي تم إنشاؤها باستخدام attachShadow۔
  3. الوضع (Mode)

    • في وضع open، يمكن لجافاسكريبت الخارجي الوصول إلى جذر الظل عبر element.shadowRoot۔ من ناحية أخرى، لا يسمح الوضع closed بالوصول۔

التنسيق داخل Shadow DOM

لـ Shadow DOM نطاق أنماطه الخاص۔ الأنماط المحددة داخل شجرة الشادو تنطبق فقط على العناصر داخل تلك الشجرة۔ إليك مثالاً:۔

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 إلى Light DOM, يتم إعادة كتابة خاصية target للحدث إلى عنصر المستضيف بدلاً من المصدر الأصلي۔
    • في هذا المثال، تكون event.target داخل Shadow DOM هي عنصر button الفعلي، ولكن خارج Shadow DOM يتم استبدالها بعنصر المستضيف div۔

Shadow DOM والعناصر المخصصة

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) تمرير محتوى 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 على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔

YouTube Video