Shadow DOM i JavaScript

Shadow DOM i JavaScript

Denne artikel forklarer Shadow DOM i 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>

Forståelse af Shadow DOM

Shadow DOM er en kraftfuld funktion i Web Components-standarden, der muliggør indkapsling af styles og DOM-struktur inden for komponenter. Denne funktion forhindrer forstyrrelser mellem styles og scripts fra komponenter og hoveddokumentet.

Shadow DOM i JavaScript

Shadow DOM giver en måde at oprette et afgrænset DOM-træ, der er knyttet til et almindeligt DOM-element. Dette skyggetræ er isoleret fra det samlede dokument, hvor eksterne styles og scripts ikke påvirker det, og de interne styles og scripts heller ikke lækker ud.

For eksempel, hvis du opretter en brugerdefineret knappekomponent ved hjælp af Shadow DOM, vil dens styles ikke forstyrre andre elementer på siden. Ligeledes vil elementer med samme klassed navn ikke komme i konflikt.

Det almindelige HTML-indhold uden for Shadow DOM kaldes Light DOM.

Fordele ved Shadow DOM

  1. Indkapsling

    • Shadow DOM adskiller stil og funktionalitet, hvilket forhindrer konflikter med globale stilarter og scripts.
  2. Genanvendelighed

    • Komponenter bygget med Shadow DOM kan genbruges på tværs af forskellige projekter uden at bekymre sig om stilkonflikter.
  3. Vedligeholdelse

    • Indkapsling gør komponentens logik og stil selvstændig, hvilket gør fejlfinding og vedligeholdelse lettere.

Oprettelse af Shadow DOM

For at bruge Shadow DOM skal du knytte en shadow root til et HTML-element. Her er et simpelt eksempel:.

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

Forklaring

Denne kode indeholder følgende elementer:.

  1. Værtelement

    • Et almindeligt DOM-element, som shadow root er knyttet til (i dette tilfælde #shadow-host).
  2. Shadow Root

    • Roden af skyggetræet, oprettet med attachShadow.
  3. Tilstand

    • I open-tilstand kan ekstern JavaScript få adgang til shadow root via element.shadowRoot. Derimod tillader closed-tilstand ikke adgang.

Styling inden for Shadow DOM

Shadow DOM har sit eget stilområde. Stile defineret inden for skyggetræet gælder kun for elementerne i det træ. Her er et eksempel:.

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

Selvom der er modstridende stilarter i hoveddokumentet, påvirker de ikke paragrafen inde i shadow tree.

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`;
  • Paragraffen inde i Shadow DOM forbliver grøn, mens den eksterne er rød.

Hændelser inden for Shadow DOM

Hændelser inden for Shadow DOM ligner almindelige DOM-hændelser, men kan opføre sig anderledes med hensyn til forplantning på grund af indkapsling.

Her er et eksempel:.

 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});
  • Når knappen klikkes, udløses begge lyttere, hvilket demonstrerer adfærden ved hændelsesbobling.
  • Når en hændelse, der opstår inde i Shadow DOM, bobler op til Light DOM, bliver hændelsens target omskrevet til værtselementet i stedet for den oprindelige kilde.
    • I dette eksempel er event.target inde i Shadow DOM det faktiske button-element, men udenfor Shadow DOM erstattes det med værtens div-element.

Shadow DOM og brugerdefinerede elementer

Shadow DOM og brugerdefinerede elementer er nøglekomponenter i Web Components. De er teknologier, der bruges til at skabe genanvendelige og indkapslede UI-komponenter.

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

Hoved­dokumentet indeholder HTML som dette:.

1<my-card></my-card>
  • Ved at bruge Shadow DOM i brugerdefinerede elementer kan du bygge genanvendelige komponenter, der er modstandsdygtige over for stil-konflikter. I denne kode oprettes et tag ved navn my-card, som associeres med klassen MyCard. <p>-elementet inde i my-card påvirkes ikke af eksterne stilarter og vises altid i blå.

Slots: Distribuering af Light DOM-indhold

Slots giver dig mulighed for at projicere Light DOM-indhold ind i Shadow DOM. Her er et eksempel:.

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

Hoved­dokumentet indeholder HTML som dette:.

1<my-element>
2    <h3 slot="header">Header Content</h1>
3    <p slot="content">Main Content</p>
4</my-element>
  • slot-elementet inden i Shadow DOM viser Light DOM-indhold, der har den tilsvarende slot-attribut.

Konklusion

Shadow DOM er et vigtigt værktøj til at bygge robuste, genanvendelige og vedligeholdelsesvenlige webkomponenter. Ved at indkapsle stilarter og funktionalitet reduceres potentialet for konflikter, og administrationen af kodebasen forenkles.

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video