Start · Sprachen · JavaScript · Referenz · ShadowRoot

ShadowRoot

Schnittstelle

Die <code>ShadowRoot</code>-Schnittstelle ist der Wurzelknoten eines gekapselten DOM-Teilbaums (Shadow DOM), der getrennt vom Haupt-DOM eines Dokuments gerendert wird.

seit JavaScript Baseline: alle modernen Browser (seit 2020) Kategorie: webapi

Signatur

interface ShadowRoot extends DocumentFragment

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 über element.shadowRoot erreichbar.
  • innerHTML: liest oder setzt das Markup des Shadow Trees.
  • adoptedStyleSheets: Array von CSSStyleSheet-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 von attachShadow().
  • getAnimations(), elementFromPoint(), getSelection() (nicht standardisiert), setHTMLUnsafe(), getHTML().
  • Event slotchange wird 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'));
true true "open" true false

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>
`);
Slot geändert: title Slot geändert: (default)

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

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);
Hallo aus dem Shadow DOM true