Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Verwendung von Templates und Slots

Dieser Artikel erklärt, wie Sie die Elemente <template> und <slot> verwenden können, um ein flexibles Template zu erstellen, das dann genutzt werden kann, um das Shadow-DOM eines Web Components zu füllen.

Die Wahrheit über Templates

Wenn Sie immer wieder dieselben Markupstrukturen auf einer Webseite wiederverwenden müssen, macht es Sinn, eine Art von Template zu verwenden, anstatt die gleiche Struktur immer wieder zu wiederholen. Dies war vorher möglich, wird aber durch das HTML-Element <template> erheblich erleichtert. Dieses Element und sein Inhalt werden nicht im DOM gerendert, können aber trotzdem mittels JavaScript referenziert werden.

Schauen wir uns ein einfaches, schnelles Beispiel an:

html
<template id="custom-paragraph">
  <p>My paragraph</p>
</template>

Dies erscheint nicht auf Ihrer Seite, bis Sie mit JavaScript eine Referenz darauf erhalten und dann an das DOM anhängen, indem Sie so etwas wie Folgendes verwenden:

js
let template = document.getElementById("custom-paragraph");
let templateContent = template.content;
document.body.appendChild(templateContent);

Obwohl trivial, können Sie bereits erkennen, wie nützlich dies sein könnte.

Verwendung von Templates mit Webkomponenten

Templates sind für sich schon nützlich, aber sie funktionieren noch besser mit Webkomponenten. Lassen Sie uns eine Webkomponente definieren, die unser Template als Inhalt ihres Shadow-DOMs verwendet. Wir nennen sie ebenfalls <my-paragraph>:

js
customElements.define(
  "my-paragraph",
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById("custom-paragraph");
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(document.importNode(templateContent, true));
    }
  },
);

Der entscheidende Punkt ist hier, dass wir einen Klon des Template-Inhalts mit der Methode Document.importNode() an den Shadow-Root anhängen.

Und da wir ihre Inhalte an ein Shadow-DOM anhängen, können wir einige Stilinformationen in das Template in einem <style>-Element einfügen, welches dann innerhalb des Custom Elements gekapselt wird. Dies würde nicht funktionieren, wenn wir es einfach dem Standard-DOM anfügen würden.

Zum Beispiel:

html
<template id="custom-paragraph">
  <style>
    p {
      color: white;
      background-color: #666666;
      padding: 5px;
    }
  </style>
  <p>My paragraph</p>
</template>

Nun können wir es verwenden, indem wir es einfach zu unserem HTML-Dokument hinzufügen:

html
<my-paragraph></my-paragraph>

Flexibilität mit Slots hinzufügen

Bisher so gut, aber das Element ist nicht sehr flexibel. Wir können nur ein Stück Text darin anzeigen, was bedeutet, dass es im Moment sogar weniger nützlich ist als ein normaler Absatz! Wir können es ermöglichen, dass unterschiedlicher Text in jeder Elementinstanz in einer schönen deklarativen Weise angezeigt werden kann, indem wir das Element <slot> verwenden.

Slots werden durch ihr name-Attribut identifiziert und ermöglichen es Ihnen, Platzhalter in Ihrem Template zu definieren, die mit jedem gewünschten Markupfragment gefüllt werden können, wenn das Element im Markup verwendet wird.

Wenn wir also einen Slot in unser triviales Beispiel hinzufügen möchten, könnten wir das Paragraphenelement unseres Templates so aktualisieren:

html
<p><slot name="my-text">My default text</slot></p>

Wenn der Inhalt des Slots nicht definiert ist, wenn das Element im Markup enthalten ist, oder wenn der Browser Slots nicht unterstützt, enthält <my-paragraph> einfach den Fallback-Text "My default text".

Um den Inhalt des Slots zu definieren, fügen wir eine HTML-Struktur innerhalb des <my-paragraph>-Elements mit einem slot-Attribut ein, dessen Wert dem Namen des Slots entspricht, den wir füllen möchten. Wie zuvor kann dies alles sein, was Sie mögen, zum Beispiel:

html
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>

oder

html
<my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>

Hinweis: Knoten, die in Slots eingefügt werden können, werden als Slottable Knoten bezeichnet; wenn ein Knoten in einen Slot eingefügt wurde, sagt man, er ist slotted.

Und das war's für unser triviales Beispiel. Wenn Sie noch ein wenig damit spielen möchten, können Sie es auf GitHub finden (sehen Sie es sich auch live an).

Das name-Attribut sollte pro Shadow-Root eindeutig sein: Wenn Sie zwei Slots mit demselben Namen haben, werden alle Elemente mit einem passenden slot-Attribut dem ersten Slot mit diesem Namen zugewiesen. Aber das slot-Attribut muss nicht eindeutig sein: Ein <slot> kann von mehreren Elementen gefüllt werden, die alle ein passendes slot-Attribut haben.

Sowohl die name- als auch die slot-Attribute standardmäßig auf den leeren String, sodass Elemente ohne slot-Attribute dem <slot> ohne name-Attribut (dem unbenannten Slot oder Standardslot) zugewiesen werden. Hier ist ein Beispiel:

html
<template id="custom-paragraph">
  <style>
    p {
      color: white;
      background-color: #666666;
      padding: 5px;
    }
  </style>
  <p>
    <slot name="my-text">My default text</slot>
    <slot></slot>
  </p>
</template>

Sie können es dann so verwenden:

html
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
  <span>This will go into the unnamed slot</span>
  <span>This will also go into the unnamed slot</span>
</my-paragraph>

In diesem Beispiel:

  • Inhalt mit slot="my-text" geht in den benannten Slot.
  • Alle anderen Inhalte gehen automatisch in den unbenannten Slot.

Ein umfangreicheres Beispiel

Um den Artikel abzuschließen, werfen wir einen Blick auf etwas weniger Triviales.

Die folgende Reihe von Code-Snippets zeigt, wie man <slot> zusammen mit <template> und etwas JavaScript verwendet, um:

  • ein <element-details>-Element mit benannten Slots in seinem shadow root zu erstellen
  • das <element-details>-Element so zu gestalten, dass es beim Einsatz in Dokumenten dargestellt wird, indem es den Inhalt des Elements mit Inhalten seines shadow root zusammensetzt — das heißt, Teile des Inhalts des Elements werden verwendet, um benannte Slots in seinem shadow root zu füllen

Beachten Sie, dass es technisch möglich ist, das <slot>-Element ohne ein <template>-Element zu verwenden, z.B. innerhalb eines regulären <div>-Elements, und dennoch die Platzhalterfunktionen des <slot> für Shadow-DOM-Inhalte zu nutzen; und dies kann tatsächlich den kleinen Aufwand vermeiden, zuerst auf die content-Eigenschaft des Template-Elements zugreifen zu müssen (und es zu klonen). Es ist jedoch in der Regel praktischer, Slots innerhalb eines <template>-Elements hinzuzufügen, da Sie wahrscheinlich kein Muster auf der Grundlage eines bereits gerenderten Elements definieren müssen.

Darüber hinaus, selbst wenn es nicht bereits gerendert ist, sollte der Zweck des Containers als Template semantisch klarer sein, wenn man das <template> verwendet. Zusätzlich kann <template> direkt Elemente wie <td> enthalten, die verschwinden würden, wenn sie zu einem <div> hinzugefügt würden.

Hinweis: Dieses vollständige Beispiel finden Sie unter element-details (sehen Sie es live ebenfalls).

Ein Template mit einigen Slots erstellen

Zuerst verwenden wir das Element <slot> innerhalb eines <template>, um ein neues "element-details-template"-Dokumentfragment zu erstellen, das einige benannte Slots enthält:

html
<template id="element-details-template">
  <style>
    details {
      font-family: "Open Sans Light", "Helvetica", "Arial";
    }
    .name {
      font-weight: bold;
      color: #217ac0;
      font-size: 120%;
    }
    h4 {
      margin: 10px 0 -8px 0;
    }
    h4 span {
      background: #217ac0;
      padding: 2px 6px;
    }
    h4 span {
      border: 1px solid #cee9f9;
      border-radius: 4px;
    }
    h4 span {
      color: white;
    }
    .attributes {
      margin-left: 22px;
      font-size: 90%;
    }
    .attributes p {
      margin-left: 16px;
      font-style: italic;
    }
  </style>
  <details>
    <summary>
      <span>
        <code class="name"
          >&lt;<slot name="element-name">NEED NAME</slot>&gt;</code
        >
        <span class="desc"
          ><slot name="description">NEED DESCRIPTION</slot></span
        >
      </span>
    </summary>
    <div class="attributes">
      <h4><span>Attributes</span></h4>
      <slot name="attributes"><p>None</p></slot>
    </div>
  </details>
  <hr />
</template>

Dieses <template>-Element besitzt mehrere Funktionen:

Ein neues <element-details> Element aus dem <template> erstellen

Als Nächstes erstellen wir ein neues benutzerdefiniertes Element namens <element-details> und verwenden Element.attachShadow, um dieses als sein shadow root zu verbinden, das Dokumentfragment, welches wir mit unserem <template>-Element oben erstellt haben. Dies verwendet exakt dasselbe Muster, das wir in unserem vorherigen trivialen Beispiel gesehen haben.

js
customElements.define(
  "element-details",
  class extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById(
        "element-details-template",
      ).content;
      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(document.importNode(template, true));
    }
  },
);

Verwendung des benutzerdefinierten Elements <element-details> mit benannten Slots

Nehmen wir jetzt das <element-details> Element und verwenden es tatsächlich in unserem Dokument:

html
<element-details>
  <span slot="element-name">slot</span>
  <span slot="description"
    >A placeholder inside a web component that users can fill with their own
    markup, with the effect of composing different DOM trees together.</span
  >
  <dl slot="attributes">
    <dt>name</dt>
    <dd>The name of the slot.</dd>
  </dl>
</element-details>

<element-details>
  <span slot="element-name">template</span>
  <span slot="description"
    >A mechanism for holding client- side content that is not to be rendered
    when a page is loaded but may subsequently be instantiated during runtime
    using JavaScript.</span
  >
</element-details>

Zu diesem Snippet, beachten Sie diese Punkte:

  • Das Snippet enthält zwei Instanzen von <element-details>-Elementen, die beide das slot-Attribut verwenden, um auf die benannten Slots "element-name" und "description" zu referenzieren, die wir im <element-details> shadow root eingefügt haben.
  • Nur das erste dieser beiden <element-details>-Elemente referenziert den "attributes" benannten Slot. Das zweite <element-details>-Element fehlt der Hinweis auf den "attributes" benannten Slot.
  • Das erste <element-details>-Element verweist auf den "attributes" benannten Slot mittels eines <dl>-Elements mit <dt> und <dd>-Kindern.

Einen letzten Hauch von Styling hinzufügen

Als abschließenden Schliff fügen wir ein wenig mehr CSS für die <dl>, <dt> und <dd>-Elemente in unserem Dokument hinzu:

css
dl {
  margin-left: 6px;
}
dt {
  color: #217ac0;
  font-family: "Consolas", "Liberation Mono", "Courier New";
  font-size: 110%;
  font-weight: bold;
}
dd {
  margin-left: 16px;
}

Ergebnis

Lassen Sie uns schließlich alle Snippets zusammenfügen und sehen, wie das gerenderte Ergebnis aussieht.

Beachten Sie die folgenden Punkte über dieses gerenderte Ergebnis:

  • Auch wenn die Instanzen des <element-details> Elements im Dokument nicht direkt das <details>-Element verwenden, werden sie unter Verwendung von <details> gerendert, weil der shadow root sie damit füllt.
  • Innerhalb der gerenderten <details>-Ausgabe wird der Inhalt in den <element-details>-Elementen dazu verwendet, die benannten Slots aus dem shadow root zu füllen. Mit anderen Worten, der DOM-Baum der <element-details>-Elemente wird zusammengesetzt mit dem Inhalt des shadow root.
  • Für beide <element-details>-Elemente wird eine Attributes-Überschrift automatisch aus dem shadow root vor der Position des "attributes" benannten Slot hinzugefügt.
  • Da das erste <element-details>-Element ein <dl>-Element hat, das explizit auf den "attributes"- benannten Slot aus seinem shadow root verweist, ersetzt der Inhalt dieses <dl> den "attributes" benannten Slot aus dem shadow root.
  • Da das zweite <element-details>-Element nicht explizit auf den "attributes" benannten Slot aus seinem shadow root verweist, wird sein Inhalt für diesen benannten Slot mit dem Standardinhalt aus dem shadow root gefüllt.