Signatur
Beschreibung
Ein ShadowRoot ist die Wurzel eines Shadow Trees, also eines DOM-Teilbaums, der an ein Host-Element gehängt ist, dem sogenannten Shadow Host. Markup, Styles und IDs im Shadow Tree sind vom übrigen Dokument abgeschottet. CSS von außen greift (mit Ausnahme vererbter Eigenschaften und CSS-Custom-Properties) nicht hinein, und Styles von innen dringen nicht nach außen. Damit ist Shadow DOM die Grundlage für Web Components.
Einen ShadowRoot erzeugt man mit element.attachShadow({ mode }). Alternativ geht das deklarativ im HTML über <template shadowrootmode="open">. Einen direkten Konstruktor gibt es nicht. Weil ShadowRoot von DocumentFragment erbt, stehen Methoden wie querySelector(), append() und getElementById() zur Verfügung.
Wichtige Eigenschaften und Methoden:
host: das Element, an dem der Shadow Root hängt.mode:"open"oder"closed". Bei"open"ist der Root von außen überelement.shadowRooterreichbar.innerHTML: liest oder setzt das Markup des Shadow Trees.adoptedStyleSheets: Array vonCSSStyleSheet-Objekten (Constructable Stylesheets), die sich mehrere Komponenten teilen können.activeElement: das fokussierte Element innerhalb des Shadow Trees.delegatesFocus,slotAssignment,clonable,serializable: schreibgeschützte Spiegelungen der Optionen vonattachShadow().getAnimations(),elementFromPoint(),getSelection()(nicht standardisiert),setHTMLUnsafe(),getHTML().- Event
slotchangewird an<slot>-Elementen im Shadow Tree ausgelöst.
Verwende Shadow DOM, wenn eine Komponente unabhängig vom umgebenden CSS und DOM funktionieren soll, etwa bei Widgets, Design-System-Bausteinen oder eingebetteten Drittanbieter-Elementen. Über <slot> lassen sich Inhalte aus dem Light DOM an bestimmten Stellen einblenden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $init.mode Pflicht | "open" | "closed" | Option von attachShadow(). Legt fest, ob element.shadowRoot den Root liefert (open) oder null (closed). |
|
| $init.delegatesFocus | boolean | false | Wenn true, wird der Fokus beim Klick auf den Host an das erste fokussierbare Element im Shadow Tree weitergereicht. |
| $init.slotAssignment | "named" | "manual" | "named" | named: Slots werden automatisch über das slot-Attribut zugeordnet. manual: Zuordnung per HTMLSlotElement.assign(). |
| $init.clonable | boolean | false | Legt fest, ob cloneNode() auf dem Host den Shadow Root mitkopiert. |
| $init.serializable | boolean | false | Legt fest, ob der Shadow Root durch getHTML({ serializableShadowRoots: true }) serialisiert werden kann. |
Beispiele
Shadow Root erzeugen und befüllen
const host = document.createElement('div');
document.body.append(host);
// Shadow Root anhängen
const shadow = host.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>p { color: crimson; font-weight: bold; }</style>
<p>Ich bin gekapselt!</p>
`;
console.log(shadow instanceof ShadowRoot);
console.log(shadow.host === host, shadow.mode);
console.log(host.shadowRoot === shadow);
// Globales querySelector sieht nicht in den Shadow Tree
console.log(document.querySelector('p') === shadow.querySelector('p'));
Web Component mit Slot und geteiltem Stylesheet
// Ein Stylesheet für alle Instanzen (Constructable Stylesheet)
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
:host { display: block; border: 1px solid #ccc; padding: 1rem; border-radius: 8px; }
h2 { margin: 0 0 .5rem; font-size: 1.1rem; }
`);
class InfoCard extends HTMLElement {
constructor() {
super();
const root = this.attachShadow({ mode: 'open' });
root.adoptedStyleSheets = [sheet];
root.innerHTML = `
<h2><slot name="title">Ohne Titel</slot></h2>
<slot></slot>
`;
// Auf Änderungen der eingeblendeten Inhalte reagieren
root.addEventListener('slotchange', (e) => {
console.log('Slot geändert:', e.target.name || '(default)');
});
}
}
customElements.define('info-card', InfoCard);
document.body.insertAdjacentHTML('beforeend', `
<info-card>
<span slot="title">Hinweis</span>
Shadow DOM kapselt Styles.
</info-card>
`);
Geschlossener Modus und Fokus-Delegation
class SecretInput extends HTMLElement {
#root;
constructor() {
super();
// Referenz nur intern halten
this.#root = this.attachShadow({ mode: 'closed', delegatesFocus: true });
this.#root.innerHTML = '<label>PIN <input type="password"></label>';
}
get value() {
return this.#root.querySelector('input').value;
}
}
customElements.define('secret-input', SecretInput);
const el = document.createElement('secret-input');
document.body.append(el);
console.log(el.shadowRoot); // von außen nicht erreichbar
el.focus(); // Fokus wandert ins <input>
console.log(document.activeElement === el);
Deklaratives Shadow DOM (HTML) auslesen
// HTML: <my-box><template shadowrootmode="open"><p>Hallo</p></template></my-box>
document.body.setHTMLUnsafe(`
<my-box>
<template shadowrootmode="open" shadowrootserializable>
<p>Hallo aus dem Shadow DOM</p>
</template>
</my-box>
`);
const box = document.querySelector('my-box');
console.log(box.shadowRoot.querySelector('p').textContent);
console.log(box.shadowRoot.serializable);