Signatur
Beschreibung
Die schreibgeschützte Eigenschaft shadowRoot des ElementInternals-Objekts liefert den ShadowRoot des zugehörigen Custom Elements. Das Objekt bekommen Sie über this.attachInternals(). Anders als Element.shadowRoot gibt die Eigenschaft auch geschlossene Shadow Roots (mode: "closed") zurück. Das ist sicher, weil nur die Komponente selbst eine Referenz auf ihr ElementInternals-Objekt hat.
Am häufigsten brauchen Sie die Eigenschaft bei Declarative Shadow DOM. Wenn der Server das Markup mit <template shadowrootmode="closed"> ausliefert, legt der Parser den Shadow Root schon an, bevor die Klasse des Custom Elements registriert ist. Einen Rückgabewert von attachShadow(), den Sie speichern könnten, gibt es dann nicht. Über internals.shadowRoot findet die Komponente den vorgerenderten Shadow Root trotzdem und kann ihn „hydratisieren“ (also mit Logik versehen), statt ihn neu aufzubauen.
Hat das Element noch keinen Shadow Root, ist der Wert null. Das gilt auch, wenn der Shadow Root für ElementInternals nicht freigegeben ist, etwa weil er vor dem Upgrade zum Custom Element per attachShadow() angelegt wurde. Typisch ist deshalb dieses Muster: erst internals.shadowRoot prüfen und nur bei null selbst attachShadow() aufrufen.
Rückgabewert
ShadowRoot des Host-Elements, egal ob offen oder geschlossen. null, wenn kein Shadow Root existiert oder er für ElementInternals nicht verfügbar ist.Beispiele
Zugriff auf einen geschlossenen Shadow Root
class SecretBox extends HTMLElement {
#internals = this.attachInternals();
constructor() {
super();
// Rückgabewert von attachShadow wird bewusst nicht gespeichert
this.attachShadow({ mode: 'closed' });
this.#internals.shadowRoot.innerHTML = '<p>Geheim!</p>';
}
connectedCallback() {
console.log('Von außen:', this.shadowRoot);
console.log('Intern:', this.#internals.shadowRoot.mode);
}
}
customElements.define('secret-box', SecretBox);
document.body.append(document.createElement('secret-box'));
Declarative Shadow DOM hydratisieren oder neu anlegen
// HTML vom Server:
// <my-counter>
// <template shadowrootmode="closed">
// <button>0</button>
// </template>
// </my-counter>
class MyCounter extends HTMLElement {
#internals = this.attachInternals();
#count = 0;
constructor() {
super();
let root = this.#internals.shadowRoot;
if (!root) {
// Kein vorgerendertes Markup: Shadow Root selbst erzeugen
root = this.attachShadow({ mode: 'closed' });
root.innerHTML = '<button>0</button>';
console.log('Client-seitig erzeugt');
} else {
console.log('Vorhandenen Shadow Root übernommen');
}
const button = root.querySelector('button');
button.addEventListener('click', () => {
button.textContent = ++this.#count;
});
}
}
customElements.define('my-counter', MyCounter);
Wert vor und nach attachShadow()
class DemoEl extends HTMLElement {
constructor() {
super();
const internals = this.attachInternals();
console.log(internals.shadowRoot); // noch kein Shadow Root
const root = this.attachShadow({ mode: 'open' });
console.log(internals.shadowRoot === root);
}
}
customElements.define('demo-el', DemoEl);
new DemoEl();
// Wichtig · Fallstricke
- Nur für autonome Custom Elements:
attachInternals()gibt es nur bei autonomen Custom Elements. Bei eingebauten Elementen mitis-Attribut oder bei normalen Elementen wirft der Aufruf einenNotSupportedError. Außerdem darfattachInternals()pro Element nur einmal aufgerufen werden. - Kapselung wahren: Speichern Sie das
ElementInternals-Objekt in einem privaten Feld (#internals), nicht in einer öffentlichen Eigenschaft. Sonst kann fremder Code darüber auf einen geschlossenen Shadow Root zugreifen, undmode: "closed"bringt nichts mehr. - Schreibgeschützt: Die Eigenschaft hat keinen Setter. Eine Zuweisung wird ignoriert bzw. wirft im Strict Mode einen
TypeError. Einen Shadow Root erzeugen Sie nur mitattachShadow()oder deklarativ per<template shadowrootmode>. - Kann
nullsein: Prüfen Sie den Wert vor der Verwendung. Nach dem Upgrade liefert die Eigenschaft auch dannnull, wenn das Element vor dem Upgrade schon einen Shadow Root perattachShadow()bekommen hat. - Kompatibilität: Wird von allen aktuellen Browsern unterstützt (Chromium 88+, Firefox 93+, Safari 16.4+). Ältere Safari-Versionen haben kein
ElementInternals. Dort helfen Feature-Erkennung ('attachInternals' in HTMLElement.prototype) oder ein Polyfill.