Kodokon kodokon.com

Web Components: Custom Elements, Shadow DOM und Slots

Beherrsche den Lebenszyklus von Custom Elements, die Kapselung durch Shadow DOM und die Inhaltsprojektion über Slots.

12 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Custom Elements erweitern das HTML-Vokabular: Jede Klasse, die von HTMLElement erbt, kann im CustomElementRegistry registriert werden, solange ihr Name einen Bindestrich enthält (user-badge, niemals userbadge). Feiner Punkt: Erscheint das Tag im Dokument, bevor customElements.define aufgerufen wird, wird es zunächst als unbekanntes Element erstellt und dann an Ort und Stelle aktualisiert (upgraded) - der Konstruktor wird also nachträglich auf einem Knoten aufgerufen, der bereits im Baum vorhanden ist.

JAVASCRIPT
class UserBadge extends HTMLElement {
  static observedAttributes = ["name"];
  connectedCallback() {
    this.render();
  }
  attributeChangedCallback() {
    this.render();
  }
  render() {
    this.textContent = this.getAttribute("name");
  }
}
customElements.define("user-badge", UserBadge);
Ein minimales Custom Element mit vollständigem Lebenszyklus.

Der Lebenszyklus hält einige Fallen bereit: attributeChangedCallback wird nur für die in observedAttributes aufgelisteten Attribute ausgelöst, und bei einem Upgrade wird es für jedes bereits vorhandene Attribut vor connectedCallback aufgerufen. Die Spezifikation verbietet dem Konstruktor, Attribute und Kinder zu untersuchen oder welche hinzuzufügen: In dem Moment, in dem er läuft (über createElement oder Klonen), kann das Element völlig leer sein.

HTML
<template id="card-tpl">
  <style>
    :host { display: block; padding: 1rem; }
    ::slotted(h2) { margin: 0; }
  </style>
  <slot name="title"></slot>
  <slot></slot>
</template>
Ein inertes Template: geparst, aber weder ausgeführt noch gerendert.

attachShadow erstellt einen gekapselten Teilbaum: Styles dringen nicht nach außen, die Selektoren der Seite reichen nicht hinein - nur vererbte CSS-Eigenschaften (color, font) und Custom Properties überqueren die Grenze. Der Inhalt eines <template> liegt in einem DocumentFragment, das über .content zugänglich ist: Seine Skripte laufen nicht und seine Bilder laden nicht, bis du es in das Dokument klonst. Knoten, die im Light DOM platziert sind, werden in die <slot>-Elemente projiziert: Sie bleiben physisch im Light DOM, und das slotchange-Ereignis benachrichtigt dich über jede Änderung der Zuordnung.

JAVASCRIPT
class InfoCard extends HTMLElement {
  constructor() {
    super();
    const tpl = document.getElementById("card-tpl");
    this.attachShadow({ mode: "open" })
      .appendChild(tpl.content.cloneNode(true));
  }
}
customElements.define("info-card", InfoCard);
Das Template in eine offene Shadow Root klonen.

Zwei Erweiterungen verdienen deine Aufmerksamkeit. Formularassoziierte Custom Elements: Mit static formAssociated = true und attachInternals() nimmt deine Komponente voll an Formularen teil (übermittelter Wert, native Validierung, :invalid-Zustände). Und Declarative Shadow DOM: <template shadowrootmode=open> hängt eine Shadow Root beim Parsen an, ohne JavaScript, was Web Components mit Server-Side Rendering kompatibel macht.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Warum darf der Konstruktor eines Custom Elements weder seine Attribute lesen noch Kinder erstellen?
    • Weil Attribute vor connectedCallback schreibgeschützt eingefroren sind
    • Weil das Shadow DOM während der Konstruktion das Light DOM verbirgt
    • Weil das Element in diesem Moment (createElement, Klonen) leer sein kann; die Spezifikation verlangt einen Konstruktor ohne Auswirkung auf den Inhalt
  2. Was gibt element.shadowRoot bei attachShadow({ mode: 'closed' }) von außen zurück?
    • null: Nur die von attachShadow zurückgegebene Referenz gibt Zugriff auf die Shadow Root
    • Die Shadow Root, aber schreibgeschützt
    • Ein leeres DocumentFragment
    • Es wird eine DOMException geworfen
  3. Wo befinden sich die in einen <slot> projizierten Knoten physisch?
    • Sie werden zum Renderzeitpunkt in das Shadow DOM verschoben
    • Sie bleiben im Light DOM; der Slot zeigt sie lediglich an seiner Position im abgeflachten Baum an
    • Sie werden geklont: Das Original bleibt im Light DOM, die Kopie liegt im Shadow DOM