Shadow DOM en JavaScript

Shadow DOM en JavaScript

Cet article explique le Shadow DOM en 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 &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>

Comprendre le Shadow DOM

Le Shadow DOM est une fonctionnalité puissante du standard des Web Components qui permet l'encapsulation des styles et de la structure DOM au sein des composants. Cette fonctionnalité empêche les interférences de styles et de scripts entre les composants et le document principal.

Shadow DOM en JavaScript

Le Shadow DOM offre un moyen de créer un arbre DOM limité associé à un élément DOM classique. Cet arbre shadow est isolé du document global : les styles et scripts externes ne l'influencent pas, et ses styles et scripts internes ne s'échappent pas.

Par exemple, si vous créez un composant bouton personnalisé en utilisant le Shadow DOM, ses styles n'interféreront pas avec les autres éléments de la page. De même, les éléments portant le même nom de classe n'entreront pas en conflit.

Le contenu HTML régulier en dehors du Shadow DOM est appelé le Light DOM.

Avantages du Shadow DOM

  1. Encapsulation

    • Le Shadow DOM sépare le style et la fonctionnalité, empêchant les conflits avec les styles et scripts globaux.
  2. Réutilisabilité

    • Les composants construits avec le Shadow DOM peuvent être réutilisés dans divers projets sans se soucier des conflits de styles.
  3. Facilité de maintenance

    • L'encapsulation rend la logique et le style du composant autonomes, ce qui facilite le débogage et la maintenance.

Créer un Shadow DOM

Pour utiliser le Shadow DOM, vous devez attacher une racine shadow à un élément HTML. Voici un exemple simple :.

1// Select the host element
2const hostElement = document.querySelector('#shadow-host');
3
4// Attach a shadow root
5const shadowRoot = hostElement.attachShadow({ mode: 'open' });

Explication

Ce code comprend les éléments suivants :.

  1. Élément hôte

    • Un élément DOM classique auquel la racine shadow est attachée (dans ce cas, #shadow-host).
  2. Racine shadow

    • La racine de l'arbre shadow créée avec attachShadow.
  3. Mode

    • En mode open, le JavaScript externe peut accéder à la racine shadow via element.shadowRoot. En revanche, le mode closed n'autorise pas l'accès.

Styliser avec le Shadow DOM

Shadow DOM a son propre périmètre de styles. Les styles définis dans l'arbre shadow s'appliquent uniquement aux éléments de cet arbre. Voici un exemple :.

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`;

Même s'il y a des styles conflictuels dans le document principal, ils n'affectent pas le paragraphe à l'intérieur de l'arbre 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`;
  • Le paragraphe à l'intérieur du Shadow DOM reste vert, tandis que celui à l'extérieur est rouge.

Les événements dans le Shadow DOM

Les événements dans le Shadow DOM sont similaires aux événements normaux du DOM, mais peuvent se comporter différemment en termes de propagation à cause de l'encapsulation.

Voici un exemple :.

 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});
  • Lorsque le bouton est cliqué, les deux écouteurs sont déclenchés, ce qui démontre le comportement de la propagation des événements.
  • Lorsqu'un événement provenant du Shadow DOM remonte vers le Light DOM, la propriété target de l'événement est réécrite en tant qu'élément hôte au lieu de la source d'origine.
    • Dans cet exemple, event.target à l'intérieur du Shadow DOM est le véritable élément button, mais à l'extérieur du Shadow DOM, il est remplacé par l'élément hôte div.

Shadow DOM et éléments personnalisés

Shadow DOM et les éléments personnalisés sont des composants clés des Web Components. Ce sont des technologies utilisées pour créer des composants UI réutilisables et encapsulés.

 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);

Le document principal contient un HTML comme celui-ci :.

1<my-card></my-card>
  • En utilisant le Shadow DOM dans des éléments personnalisés, vous pouvez créer des composants réutilisables qui résistent aux conflits de styles. Dans ce code, une balise nommée my-card est créée et associée à la classe MyCard. L'élément <p> à l'intérieur de my-card n'est pas affecté par les styles externes et s'affiche toujours en bleu.

Slots : distribution du contenu du Light DOM

Les slots vous permettent de projeter le contenu du Light DOM dans le Shadow DOM. Voici un exemple :.

 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);

Le document principal contient un HTML comme celui-ci :.

1<my-element>
2    <h3 slot="header">Header Content</h1>
3    <p slot="content">Main Content</p>
4</my-element>
  • L'élément slot à l'intérieur du Shadow DOM affiche le contenu du Light DOM qui possède l'attribut slot correspondant.

Conclusion

Shadow DOM est un outil essentiel pour créer des composants web robustes, réutilisables et maintenables. En encapsulant les styles et les fonctionnalités, il réduit les risques de conflits et simplifie la gestion du code.

Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.

YouTube Video