Shadow DOM in JavaScript

Shadow DOM in JavaScript

Questo articolo spiega il Shadow DOM in 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>

Comprendere il Shadow DOM

Il Shadow DOM è una funzionalità potente dello standard dei Web Components, che consente l'incapsulamento degli stili e della struttura DOM all'interno dei componenti. Questa funzionalità impedisce interferenze tra stili e script dei componenti e il documento principale.

Shadow DOM in JavaScript

Il Shadow DOM offre un modo per creare un albero DOM limitato associato a un elemento DOM normale. Questo albero ombra è isolato dal documento generale: gli stili e gli script esterni non lo influenzano e i suoi stili e script interni non si propagano.

Ad esempio, se crei un componente personalizzato per un pulsante usando il Shadow DOM, i suoi stili non interferiranno con altri elementi della pagina. Allo stesso modo, elementi con lo stesso nome di classe non entreranno in conflitto.

Il contenuto HTML regolare al di fuori del Shadow DOM è chiamato Light DOM.

Benefici del Shadow DOM

  1. Incapsulamento

    • Il Shadow DOM separa stile e funzionalità, prevenendo conflitti con stili e script globali.
  2. Riutilizzabilità

    • I componenti costruiti con il Shadow DOM possono essere riutilizzati in diversi progetti senza preoccuparsi di conflitti di stile.
  3. Manutenibilità

    • L'incapsulamento rende logica e stile del componente autonomi, facilitando il debugging e la manutenzione.

Creazione del Shadow DOM

Per usare il Shadow DOM, è necessario collegare una radice ombra a un elemento HTML. Ecco un esempio semplice:.

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

Spiegazione

Questo codice include i seguenti elementi:.

  1. Elemento Host

    • Un elemento DOM regolare a cui viene collegata la radice shadow (in questo caso, #shadow-host).
  2. Shadow Root

    • La radice dell'albero shadow creata tramite attachShadow.
  3. Modalità

    • In modalità open, JavaScript esterno può accedere alla shadow root tramite element.shadowRoot. D'altro canto, la modalità closed non consente l'accesso.

Stilizzazione all'interno del Shadow DOM

Il Shadow DOM ha il proprio ambito di stile. Gli stili definiti all'interno dell'albero ombra si applicano solo agli elementi di quell'albero. Ecco un esempio:.

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

Anche se ci sono stili in conflitto nel documento principale, non influenzano il paragrafo all'interno dell'albero 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`;
  • Il paragrafo all'interno del Shadow DOM rimane verde, mentre quello esterno è rosso.

Eventi all'interno del Shadow DOM

Gli eventi all'interno del Shadow DOM sono simili agli eventi del DOM regolare ma possono comportarsi diversamente in termini di propagazione a causa dell'incapsulamento.

Ecco un esempio:.

 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});
  • Quando si clicca sul pulsante, entrambi i listener vengono attivati, dimostrando il comportamento della propagazione degli eventi.
  • Quando un evento originato all'interno dello Shadow DOM si propaga verso il Light DOM, il suo target viene riscritto all'elemento host invece della sorgente originale.
    • In questo esempio, event.target all'interno dello Shadow DOM è effettivamente l'elemento button, ma al di fuori dello Shadow DOM viene sostituito con l'elemento host div.

Shadow DOM ed Elementi Personalizzati

Shadow DOM e gli elementi personalizzati sono componenti chiave dei Web Components. Sono tecnologie utilizzate per creare componenti UI riutilizzabili e incapsulati.

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

Il documento principale contiene HTML come questo:.

1<my-card></my-card>
  • Utilizzando lo Shadow DOM all'interno degli elementi personalizzati, puoi creare componenti riutilizzabili resistenti ai conflitti di stile. In questo codice, viene creata una tag chiamata my-card e associata alla classe MyCard. L'elemento <p> all'interno di my-card non è influenzato dagli stili esterni ed è sempre visualizzato in blu.

Slot: Distribuire il contenuto del Light DOM

Gli slot permettono di proiettare il contenuto del Light DOM dentro il Shadow DOM. Ecco un esempio:.

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

Il documento principale contiene HTML come questo:.

1<my-element>
2    <h3 slot="header">Header Content</h1>
3    <p slot="content">Main Content</p>
4</my-element>
  • L'elemento slot all'interno del Shadow DOM mostra il contenuto del Light DOM che ha l'attributo slot corrispondente.

Conclusione

Il Shadow DOM è uno strumento fondamentale per costruire componenti web robusti, riutilizzabili e manutenibili. Incapsulando stili e funzionalità, riduce il rischio di conflitti e semplifica la gestione del codice.

Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.

YouTube Video