// 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.
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.
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
| API | Gibt Ihnen |
|---|---|
| Custom Elements | Eigene Tags mit Lebenszyklus-Callbacks |
| Shadow DOM | Stil- 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.