</> HTML5Advent
ENFRESDEITPT

// html · Web Platform Advent #5

Web Components: Custom Elements, Shadow DOM und Templates

Erstellen Sie wiederverwendbare HTML-Elemente mit den nativen Web-Components-APIs — ein Custom Element definieren, Stile mit dem Shadow DOM kapseln, ein <template> klonen und Daten über Attribute übergeben.

Ein Haufen bunter ineinandergreifender Bausteine aus Kunststoff in verschiedenen Formen

Web Components sind eine Reihe nativer Browser-APIs, mit denen Sie Ihre eigenen wiederverwendbaren HTML-Elemente erstellen können — ganz ohne Framework. Eine Komponente, die Sie ausliefern, funktioniert gleich in reinem HTML, React, Vue oder Svelte, weil sie einfach ein Element ist, das der Browser versteht. Drei APIs machen es möglich: Custom Elements, das Shadow DOM und das <template>-Tag.

Custom Elements

Ein Custom Element ist eine Klasse, die HTMLElement erweitert und mit einem Tag-Namen registriert wird, der einen Bindestrich enthalten muss (damit er niemals mit einem künftigen eingebauten Tag kollidieren kann):

class GreetingCard extends HTMLElement {
  connectedCallback() {
    const p = document.createElement('p');
    p.textContent = 'Hello from a custom element!';
    this.append(p);
  }
}

customElements.define('greeting-card', GreetingCard);

Einmal definiert, verwenden Sie es wie jedes andere Tag:

<greeting-card></greeting-card>

Die Klasse gibt Ihnen Lebenszyklus-Callbacks. Die nützlichsten sind connectedCallback (das Element wurde der Seite hinzugefügt), disconnectedCallback (es wurde entfernt) und attributeChangedCallback (ein beobachtetes Attribut hat sich geändert).

Auf Attribute reagieren

Um Daten zu übergeben, lesen Sie Attribute — genau wie src bei einem Bild. Geben Sie mit einem statischen observedAttributes-Getter an, welche zu beobachten sind, und reagieren Sie dann in attributeChangedCallback. Den Knoten mit textContent aufzubauen hält vom Nutzer gelieferte Werte sicher:

class GreetingCard extends HTMLElement {
  static get observedAttributes() {
    return ['name'];
  }
  attributeChangedCallback(attr, oldVal, newVal) {
    if (attr === 'name') this.render();
  }
  connectedCallback() {
    this.render();
  }
  render() {
    const name = this.getAttribute('name') || 'friend';
    this.replaceChildren();
    const p = document.createElement('p');
    p.textContent = 'Hello, ' + name + '!';
    this.append(p);
  }
}
customElements.define('greeting-card', GreetingCard);

Nun rendert <greeting-card name="Ada"></greeting-card> "Hello, Ada!", und das Attribut live zu ändern rendert es neu.

An open metal toolbox with neatly organised tools in separate compartments
Eine Web Component ist wie ein Werkzeug in einem Werkzeugkasten: eine in sich geschlossene, beschriftete Einheit, nach der Sie greifen und die Sie über viele Projekte hinweg wiederverwenden, ohne sie jedes Mal neu zu bauen.

Das Shadow DOM: echte Kapselung

Das Shadow DOM gibt einem Element einen privaten Teilbaum, dessen Stile und Markup vom Rest der Seite isoliert sind. Ein <style> darin kann niemals nach außen dringen, und externes CSS kann niemals versehentlich hineinreichen. Bauen Sie es mit der DOM-API und einem <slot> für projizierten Inhalt:

class FancyButton extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });

    const style = document.createElement('style');
    style.textContent =
      'button { background:#2563eb; color:#fff; border:0;' +
      ' border-radius:8px; padding:.6rem 1rem; }';

    const button = document.createElement('button');
    button.append(document.createElement('slot'));

    shadow.append(style, button);
  }
}
customElements.define('fancy-button', FancyButton);

Der <slot> ist ein Platzhalter: Was auch immer Sie zwischen die Tags setzen — <fancy-button>Save</fancy-button> — wird an diese Stelle projiziert, sodass die Komponente Ihren Inhalt umschließt, ohne ihn zu ersetzen.

Templates: einmal klonen, günstig wiederverwenden

Das <template>-Element enthält inertes Markup, das geparst, aber nicht gerendert wird, bis Sie es klonen. Es ist der effiziente Weg, wiederkehrende Strukturen auszustanzen:

<template id="row-tpl">
  <li><span class="label"></span></li>
</template>

<script>
  const tpl = document.getElementById('row-tpl');
  const node = tpl.content.cloneNode(true);
  node.querySelector('.label').textContent = 'Item 1';
  document.querySelector('ul').append(node);
</script>

Die drei Teile zusammen

APIGibt Ihnen
Custom ElementsEigene Tags mit Lebenszyklus-Callbacks
Shadow DOMStil- und Markup-Kapselung, Slots
<template>Wiederverwendbares, klonbares inertes Markup

Sie brauchen selten alle drei auf einmal. Ein einfacher Wrapper verwendet vielleicht nur ein Custom Element; ein gestyltes, in sich geschlossenes Widget kombiniert ein Custom Element mit dem Shadow DOM und einem Slot. Da das Ergebnis reines DOM ist, fügt es sich in jeden Stack ein und überlebt jedes Framework, das in dem Jahr beliebt war, in dem Sie es geschrieben haben.