Vorlage, Anzeigenfläche und Schatten

Der Vorteil von Webkomponenten ist ihre Wiederverwendbarkeit: Sie können ein UI-Widget einmal erstellen und es mehrmals verwenden. Während Sie zum Erstellen von Webkomponenten, keine JavaScript-Bibliothek. HTML und die zugehörigen APIs bieten alles, was Sie benötigen.

Der Standard für Webkomponenten besteht aus drei Teilen: HTML-Vorlagen, Benutzerdefinierte Elemente und das Shadow DOM. In Kombination ermöglichen sie das Erstellen individueller, in sich geschlossener, wiederverwendbarer Elemente, die nahtlos integriert werden können. wie alle anderen HTML-Elemente, die wir bereits behandelt haben.

In diesem Abschnitt erstellen wir das <star-rating>-Element. Dies ist eine Webkomponente, mit der Nutzer die Nutzererfahrung auf einem auf einer Skala von eins bis fünf Sternen. Bei der Benennung eines benutzerdefinierten Elements sollten ausschließlich Kleinbuchstaben verwendet werden. Fügen Sie auch einen Bindestrich ein, da dies hilft, zwischen regulären und benutzerdefinierten Elementen zu unterscheiden.

Wir zeigen Ihnen, wie Sie die Elemente <template> und <slot>, das Attribut slot und JavaScript verwenden, um eine Vorlage mit Ein gekapseltes Shadow DOM. Wir verwenden das definierte Element wieder, passen einen Textabschnitt an, wie jedes andere Element oder jede Webkomponente. Wir werden auch kurz die Verwendung von CSS innerhalb und außerhalb des benutzerdefinierten Elements erörtern.

Das <template>-Element

Mit dem Element <template> werden HTML-Fragmente deklariert, die geklont und mit JavaScript in das DOM eingefügt werden sollen. Der Inhalt des Elements wird nicht standardmäßig gerendert. Stattdessen werden sie mit JavaScript instanziiert.

<template id="star-rating-template">
  <form>
    <fieldset>
      <legend>Rate your experience:</legend>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required />
        <input type="radio" name="rating" value="2" aria-label="2 stars" />
        <input type="radio" name="rating" value="3" aria-label="3 stars" />
        <input type="radio" name="rating" value="4" aria-label="4 stars" />
        <input type="radio" name="rating" value="5" aria-label="5 stars" />
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

Da der Inhalt eines <template>-Elements nicht auf den Bildschirm geschrieben wird, werden das <form> und sein Inhalt nicht gerendert. Ja, dieser Codepen ist leer, aber auf dem HTML-Tab finden Sie das <template>-Markup.

In diesem Beispiel ist <form> kein untergeordnetes Element von <template> im DOM. Inhalte von <template>-Elementen sind vielmehr untergeordnete Elemente. eines DocumentFragment, das von HTMLTemplateElement.content zurückgegeben wurde Property. Damit der Inhalt sichtbar wird, muss JavaScript verwendet werden, um den Inhalt zu erfassen und an das DOM anzuhängen.

Dieses kurze JavaScript hat kein benutzerdefiniertes Element erstellt. Bei diesem Beispiel wurde stattdessen der Inhalt von <template> an <body> angehängt. Der Inhalt ist jetzt Teil des sichtbaren, anpassbaren DOM.

Screenshot des vorherigen Codepens, wie im DOM gezeigt.

Es ist nicht sehr nützlich, JavaScript zu verwenden, um eine Vorlage nur für eine Bewertung mit Sternen zu implementieren. Sie erstellen jedoch eine Webkomponente für eine wiederholt verwendet wird, ist ein anpassbares Bewertungs-Widget hilfreich.

Das <slot>-Element

Wir fügen einen Slot mit einer benutzerdefinierten Legende pro Vorkommen hinzu. HTML bietet eine <slot> -Element als Platzhalter innerhalb einer <template>. Wenn ein Name angegeben wird, wird eine "benannte Anzeigenfläche" erstellt. Ein benannter Slot kann um Inhalte in einer Webkomponente anzupassen. Mit dem <slot>-Element lässt sich steuern, wo sich die untergeordneten Elemente -Element innerhalb seiner Schattenstruktur eingefügt werden.

In unserer Vorlage ändern wir <legend> in <slot>:

<template id="star-rating-template">
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>

Das Attribut name wird verwendet, um anderen Elementen Flächen zuzuweisen, wenn das Element ein slot-Attribut hat, dessen Wert mit dem einer benannten Anzeigenfläche. Wenn das benutzerdefinierte Element keine Übereinstimmung für eine Anzeigenfläche hat, wird der Inhalt von <slot> gerendert. Deshalb haben wir ein <legend> mit generischen Inhalten eingefügt, die gerendert werden können, wenn jemand einfach <star-rating></star-rating> ohne Inhalt in seinen HTML-Code einfügt.

<star-rating>
  <legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Toasty McToastface</legend>
  <p>Is this text visible?</p>
</star-rating>

Das Attribut slot ist ein globales Attribut, das verwendet wird, um den Inhalt von <slot> in <template> zu ersetzen. In unserem benutzerdefinierten Element wird das Element mit dem Attribut „slot“ ist ein <legend>. Das muss nicht so sein. In unserer Vorlage wird <slot name="star-rating-legend"> durch <anyElement slot="star-rating-legend"> ersetzt. Dabei kann <anyElement> ein beliebiges Element sein, auch ein anderes benutzerdefiniertes Element.

Nicht definierte Elemente

In der <template> haben wir ein <rating>-Element verwendet. Dies ist kein benutzerdefiniertes Element. Es handelt sich vielmehr um ein unbekanntes Element. Browser nicht scheitern, wenn sie ein Element nicht erkennen. Nicht erkannte HTML-Elemente werden vom Browser als anonym behandelt -Elemente, die mit CSS formatiert werden können. Ähnlich wie bei <span> wurde bei den Elementen <rating> und <star-rating> kein User-Agent angewendet Stils oder der Semantik.

<template> und Inhalte werden nicht gerendert. Das <template> ist ein bekanntes Element mit Inhalten, die nicht gerendert werden soll. Das <star-rating>-Element muss noch definiert werden. Solange ein Element noch nicht definiert ist, wird es im Browser angezeigt. wie alle nicht erkannten Elemente. Vorerst wird das nicht erkannte <star-rating>-Element als anonymes Inline-Element behandelt. einschließlich Legenden und <p> im dritten <star-rating> werden so angezeigt, als wären sie stattdessen in einem <span> enthalten.

Definieren wir unser Element, um dieses nicht erkannte Element in ein benutzerdefiniertes Element umzuwandeln.

Benutzerdefinierte Elemente

JavaScript ist erforderlich, um benutzerdefinierte Elemente zu definieren. Wenn definiert, wird der Inhalt des <star-rating>-Elements durch einen Shadow-Stamm, der den gesamten Inhalt der entsprechenden Vorlage enthält. Die <slot>-Elemente aus der Vorlage werden ersetzt durch den Inhalt des Elements in der <star-rating>, dessen slot-Attributwert mit dem Namenswert von <slot> übereinstimmt, wenn Es gibt eine. Andernfalls wird der Inhalt der Anzeigenflächen der Vorlage angezeigt.

Inhalte in einem benutzerdefinierten Element, das nicht mit einer Anzeigenfläche verknüpft ist (dem <p>Is this text visible?</p> in unserer dritten <star-rating>), sind nicht enthalten in Schattenstamm und daher nicht angezeigt.

Wir definieren das benutzerdefinierte Element mit dem Namen star-rating. durch Verlängern von HTMLElement:

customElements.define('star-rating',
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const starRating = document.getElementById('star-rating-template').content;
      const shadowRoot = this.attachShadow({
        mode: 'open'
      });
      shadowRoot.appendChild(starRating.cloneNode(true));
    }
  });

Nachdem das Element nun definiert wurde, wird es jedes Mal, wenn der Browser auf ein <star-rating>-Element stößt, wie definiert gerendert. durch das Element mit dem #star-rating-template, das unsere Vorlage ist. Der Browser hängt dem Knoten einen Schatten-DOM-Baum an, der Einen Klon des Vorlageninhalts in das Shadow DOM. Hinweis: Die Elemente, für die Sie attachShadow() nutzen können, sind eingeschränkt.

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(starRating.cloneNode(true));

Wenn Sie sich die Entwicklertools ansehen, werden Sie feststellen, dass das <form> aus dem <template> Teil des Schattenstamms jedes benutzerdefinierten Elements ist. Ein Klon der <template>-Inhalte ist in den Entwicklertools in jedem benutzerdefinierten Element und im Browser sichtbar, aber der Inhalt des benutzerdefinierten Elements werden nicht auf dem Bildschirm gerendert.

Screenshot der Entwicklertools mit dem Inhalt der geklonten Vorlage in jedem benutzerdefinierten Element

Im <template>-Beispiel haben wir den Vorlageninhalt an den Dokumenttext angehängt und damit zum regulären DOM. In der customElements-Definition haben wir dasselbe appendChild(), wobei der Inhalt der geklonten Vorlage an einen gekapseltem Shadow DOM.

Merken Sie sich, dass die Sterne dann wieder unformatierte Optionsfelder waren? Da sie Teil eines Shadow DOM und nicht des Standard-DOM ist, wird der Stil auf dem CSS-Tab von Codepen nicht angewendet. CSS des Tabs Stile beziehen sich auf das Dokument und nicht auf das Shadow DOM. Daher werden die Stile nicht angewendet. Wir müssen kapselte Stile, um den kapselten Shadow DOM-Inhalt zu gestalten.

Schatten-DOM

Mit dem Shadow DOM werden CSS-Stile auf jeden Schattenbaum angewendet und so vom Rest des Dokuments isoliert. Das bedeutet, dass externe CSS nicht auf Ihre Komponente angewendet wird und Komponentenstile keine Auswirkungen auf den Rest des Dokuments haben, es sei denn, wir an den sie verweisen sollen.

Da wir den Inhalt an ein Shadow DOM angehängt haben, können wir ein <style>-Element hinzufügen. gekapselten CSS für das benutzerdefinierte Element bereitstellen.

Da wir uns auf das benutzerdefinierte Element beschränken, müssen wir uns nicht darum kümmern, ob die Stile auf den Rest des Dokuments angewendet werden. Wir können die Spezifität der Selektoren erheblich reduzieren. Da die einzigen Eingaben im benutzerdefinierten Element beispielsweise Optionsfelder sind, können wir input anstelle von input[type="radio"] als Selektor verwenden.

 <template id="star-rating-template">
  <style>
    rating {
      display: inline-flex;
    }
    input {
      appearance: none;
      margin: 0;
      box-shadow: none;
    }
    input::after {
      content: '\2605'; /* solid star */
      font-size: 32px;
    }
    rating:hover input:invalid::after,
    rating:focus-within input:invalid::after {
      color: #888;
    }
    input:invalid::after,
      rating:hover input:hover ~ input:invalid::after,
      input:focus ~ input:invalid::after  {
      color: #ddd;
    }
    input:valid {
      color: orange;
    }
    input:checked ~ input:not(:checked)::after {
      color: #ccc;
      content: '\2606'; /* hollow star */
    }
  </style>
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required/>
        <input type="radio" name="rating" value="2" aria-label="2 stars"/>
        <input type="radio" name="rating" value="3" aria-label="3 stars"/>
        <input type="radio" name="rating" value="4" aria-label="4 stars"/>
        <input type="radio" name="rating" value="5" aria-label="5 stars"/>
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

Während Webkomponenten mit In-<template>-Markup und CSS-Stile gekapselt sind, beziehen sie sich auf das Shadow DOM und werden ausgeblendet. von allen Elementen außerhalb der Komponenten, dem Inhalt der Anzeigenfläche, der gerendert wird, dem <anyElement slot="star-rating-legend"> von <star-rating>, ist nicht gekapselt.

Stile außerhalb des aktuellen Bereichs

Es ist möglich, aber nicht einfach, das Dokument innerhalb eines Shadow DOM zu gestalten und den Inhalt eines Shadow DOM von dort aus zu gestalten. die globalen Stile. Die Schattengrenze, an der das Schatten-DOM endet und das reguläre DOM beginnt, kann durchlaufen werden, jedoch nur sehr bewusst.

Der Schattenbaum ist die DOM-Struktur im Schatten-DOM. Die Schattenwurzel ist der Wurzelknoten des Schattenbaums.

Mit der Pseudoklasse :host wird <star-rating> ausgewählt, das Schattenhostelement. Der Shadow Host ist der DOM-Knoten, mit dem das Shadow DOM verknüpft ist. Wenn Sie das Targeting nur auf bestimmte Versionen des Hosts vornehmen möchten, verwenden Sie :host(). Dadurch werden nur die Schattenhostelemente ausgewählt, die mit dem übergebenen Parameter übereinstimmen, z. B. ein Klassen- oder Attributselektor. Zum Auswählen allen benutzerdefinierten Elementen enthält, können Sie star-rating { /* styles */ } im globalen CSS oder :host(:not(#nonExistantId)) in den Vorlagenstilen verwenden. In Bezug auf der Spezifität gilt, gewinnt das globale Preisvergleichsportal.

Das Pseudoelement ::slotted() überschreitet die Shadow-DOM-Grenze aus dem Shadow DOM. Damit wird ein Slotted-Element ausgewählt, wenn es mit dem Selector übereinstimmt. In unserem Beispiel stimmt ::slotted(legend) mit unseren drei Legenden überein.

Um ein Schatten-DOM aus CSS im globalen Geltungsbereich auszuwählen, muss die Vorlage bearbeitet werden. Die part können Sie jedem Element hinzufügen, das Sie gestalten möchten. Verwenden Sie dann das Pseudoelement ::part(). , um Elemente in einer Schattenstruktur abzugleichen, die dem übergebenen Parameter entsprechen. Das Anker- oder Ursprungselement für das Pseudoelement ist den Namen des Hosts oder des benutzerdefinierten Elements, in diesem Fall star-rating. Der Parameter ist der Wert des Attributs part.

Würde unser Vorlagen-Markup so beginnen:

<template id="star-rating-template">
  <form part="formPart">
    <fieldset part="fieldsetPart">

Wir könnten Folgendes auf <form> und <fieldset> ausrichten:

star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }

Teilenamen verhalten sich ähnlich wie Klassen: Ein Element kann mehrere durch Leerzeichen getrennte Teilnamen haben und mehrere Elemente können mit demselben Teilnamen.

Google bietet eine praktische Checkliste zum Erstellen benutzerdefinierter Elemente. Vielleicht möchten Sie auch Informationen zu deklarativen Shadow DOMs

Wissen testen

Testen Sie Ihr Wissen über Vorlagen, Slots und Schatten.

Standardmäßig werden mit Stilen von außerhalb des Shadow DOM die Elemente im Innenbereich gestaltet.

Richtig
Bitte versuchen Sie es noch einmal.
Falsch
Richtig!

Welche Antwort beschreibt das Element „<template>“ richtig?

Ein generisches Element, mit dem beliebige Inhalte auf Ihrer Seite angezeigt werden.
Bitte versuchen Sie es noch einmal.
Ein Platzhalterelement.
Bitte versuchen Sie es noch einmal.
Ein Element, das zur Deklaration von HTML-Fragmenten verwendet wird, die nicht standardmäßig gerendert werden.
Richtig!