Shadow DOM 101

Dominic Cooney
Dominic Cooney

Einführung

Webkomponenten sind eine Reihe hochmoderner Standards, die:

  1. Die Erstellung von Widgets ermöglichen
  2. ...die zuverlässig wiederverwendet werden können
  3. ...und die Seiten werden nicht unterbrochen, wenn die nächste Version der Komponente Änderungen an internen Implementierungsdetails.

Heißt das, Sie müssen entscheiden, wann HTML/JavaScript verwendet werden soll Wann werden Webkomponenten verwendet? Nein! HTML und JavaScript können interaktive visuelle Elemente. Widgets sind interaktive visuelle Elemente. Es ist es sinnvoll, Ihre HTML- und JavaScript-Kenntnisse beim die Entwicklung eines Widgets. Die Webkomponenten-Standards sollen tun Sie das.

Es gibt jedoch ein grundlegendes Problem, das dafür sorgt, dass Widgets HTML und JavaScript sind schwer zu benutzen: Die DOM-Baumstruktur in einem Widget der mit dem Rest der Seite kapselt ist. Diese fehlende Datenkapselung bedeutet, dass Ihr Stylesheet möglicherweise versehentlich auf Teile angewendet wird, im Widget Teile des JavaScript-Codes möglicherweise versehentlich Teile im Widget Ihre IDs können sich mit den IDs im Widget überschneiden. und so weiter.

Webkomponenten bestehen aus drei Teilen:

  1. Vorlagen
  2. Schatten-DOM
  3. Benutzerdefinierte Elemente

Shadow DOM ist das Problem der DOM-Baumkapselung. Die vier Teile von Webkomponenten sind darauf ausgelegt, zusammen zu funktionieren. kann auch auswählen, welche Teile der Webkomponenten verwendet werden sollen. Dieses Anleitung zum Verwenden von Shadow DOM.

Hallo, Schattenwelt

Mit Shadow DOM können Elemente eine neue Art von Knoten erhalten, der mit . Diese neue Art von Knoten wird als Schattenstamm bezeichnet. Ein Element, dem ein Schattenstamm zugeordnet ist, wird als Schatten bezeichnet. Organisator. Der Inhalt eines Schattenhosts wird nicht gerendert. der Inhalt von wird stattdessen der Schattenstamm gerendert.

Angenommen, Sie verwenden Markups wie folgt:

<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>

dann statt der

<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
  Array.prototype.forEach.call(
      document.querySelectorAll(selector),
      function (node) { node.parentNode.removeChild(node); });
}

if (!HTMLElement.prototype.createShadowRoot) {
  remove('#ex1a');
  document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>

sieht Ihre Seite so aus:

<button id="ex1b">Hello, world!</button>
<script>
(function () {
  if (!HTMLElement.prototype.createShadowRoot) {
    remove('#ex1b');
    document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
    return;
  }
  var host = document.querySelector('#ex1b');
  var root = host.createShadowRoot();
  root.textContent = 'こんにちは、影の世界!';
})();
</script>

Und wenn der JavaScript-Code auf der Seite fragt, textContent ist, sie wird nicht „こんんんちち界界!“, aber „Hallo, Welt!“ weil die DOM-Unterstruktur unter dem Schattenstamm gekapselt ist.

Trennen von Inhalten von Präsentationen

Sehen wir uns nun an, wie Sie mit Shadow DOM Inhalte von zu präsentieren. Nehmen wir an, wir haben folgendes Namens-Tag:

<style>
.ex2a.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.ex2a .boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.ex2a .name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

Hier ist das Markup. Das würdet ihr heute schreiben. Nicht Shadow DOM verwenden:

<style>
.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

Da der DOM-Baum nicht gekapselt ist, kann die gesamte Struktur der Name-Tag für das Dokument sichtbar ist. Wenn andere Elemente auf der Seite für Stile oder Skripterstellung versehentlich dieselben Klassennamen verwendet, wird es echt schade sein.

Wir können peinliche Momente vermeiden.

Schritt 1: Präsentationsdetails ausblenden

Semantisch interessiert uns wahrscheinlich nur Folgendes:

  • Es ist ein Namens-Tag.
  • Der Name ist „Bernd“.

Zunächst schreiben wir Markups, die der gewünschten tatsächlichen Semantik näher kommen:

<div id="nameTag">Bob</div>

Dann fügen wir alle für die Präsentation verwendeten Stile und div-Elemente in ein <template>-Element:

<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
  border: 2px solid brown;

  … same as above …

</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div></span>
</template>

An dieser Stelle wird nur „Bob“ gerendert. Weil wir die DOM-Präsentationselemente nach innen verschoben haben, ein <template>-Element ist, werden sie nicht gerendert, aber Sie können über JavaScript aufgerufen werden. Das tun wir jetzt, um den Schattenstamm hinzufügen:

<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);

Nachdem Sie nun einen Schattenstamm eingerichtet haben, wird das Name-Tag gerendert. noch einmal. Mit einem Rechtsklick auf das Name-Tag sehen Sie, dass es eine süße, semantische Auszeichnung ist:

<div id="nameTag">Bob</div>

Dies zeigt, dass wir durch die Verwendung von Shadow DOM Präsentationsdetails des Name-Tags aus dem Dokument. Die Präsentationsdetails sind im Shadow DOM gekapselt.

Schritt 2: Inhalte von der Präsentation trennen

Unser Name-Tag blendet jetzt Präsentationsdetails auf der Seite aus, aber es wird trennt die Präsentation nicht wirklich vom Inhalt, der Inhalt (der Name „Bob“) sich auf der Seite befindet, also der Name, den wir in den Schattenstamm kopiert haben. Wenn wir den Parameter „Name“ im Namens-Tag angeben, müssten wir dies an zwei Stellen tun, nicht mehr synchron sind.

HTML-Elemente sind zusammengesetzte Elemente: Sie können eine Schaltfläche in eine Tabelle einfügen, . Die Komposition wird hier benötigt: Das Name-Tag muss ein die Zusammensetzung des roten Hintergrunds, Text und der Inhalt die sich im Namens-Tag befindet.

Als Komponentenautor legst du fest, wie die Komposition mit dem neuen Element <content>. Dieses eine Einfügungsstelle in der Präsentation des Widgets erstellt und der Einfügungspunkt wählt Inhalte aus dem Schattenhost für die Präsentation aus. an diesem Punkt.

Wenn wir die Auszeichnung im Shadow DOM so ändern:

<span class="unchanged"><template id="nameTagTemplate">
<style>
  …
</style></span>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    <content></content>
  </div>
</div>
<span class="unchanged"></template></span>

Wenn das Name-Tag gerendert wird, ist der Inhalt des Schattenhosts an die Stelle projiziert, an der das <content>-Element angezeigt wird.

Jetzt ist die Struktur des Dokuments einfacher, da der Name nur an einem Ort – im Dokument. Falls Ihre Seite einmal aktualisiert werden muss, Nutzername eingeben, schreiben Sie einfach:

document.querySelector('#nameTag').textContent = 'Shellie';

Und das wars auch schon. Die Darstellung des Name-Tags wird automatisch aktualisiert. da der Inhalt des Feeds prognostiziert wird, Name-Tag mit <content> eingefügt.

<div id="ex2b">

Jetzt haben wir die Trennung von Inhalt und Präsentation erreicht. Die Inhalt im Dokument ist; Die Präsentation befindet sich im Shadow DOM. Sie werden automatisch vom Browser synchronisiert, wenn es an die Zeit kommt. um etwas zu rendern.

Schritt 3: Gewinn

Durch die Trennung von Inhalt und Präsentation der den Inhalt manipuliert – im Name-Tag-Beispiel, nur mit einer einfachen Struktur zu tun haben, eine <div> statt mehrerer.

Wenn wir jetzt unsere Präsentation ändern, Code!

Angenommen, wir möchten unser Namens-Tag lokalisieren. Es ist noch ein Name damit sich der semantische Inhalt im Dokument nicht ändert:

<div id="nameTag">Bob</div>

Der Einrichtungscode des Shadow-Stamms bleibt unverändert. Was in den Änderungen am Schattenstamm:

<template id="nameTagTemplate">
<style>
.outer {
  border: 2px solid pink;
  border-radius: 1em;
  background: url(sakura.jpg);
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
  font-family: sans-serif;
  font-weight: bold;
}
.name {
  font-size: 45pt;
  font-weight: normal;
  margin-top: 0.8em;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="name">
    <content></content>
  </div>
  と申します。
</div>
</template>

Dies ist eine große Verbesserung gegenüber der heutigen Situation im Web, da kann der Code für die Namensaktualisierung von der Struktur des Komponente, die einfach und einheitlich ist. Ihr Name der Aktualisierungscode nicht die für die Rendering. Wenn wir das gerenderte Element berücksichtigen, erscheint der Name auf Englisch (nach „Hi! Mein Name ist“), aber zuerst auf Japanisch. (vor „備名申まん“). Diese Unterscheidung ist semantisch bedeutungslos um den angezeigten Namen zu aktualisieren, sodass der Namensaktualisierungscode diese Details nicht kennen muss.

Zusätzlicher Schritt: Erweiterte Projektion

Im obigen Beispiel hat das Element <content> alle Inhalte des Schattenhosts auswählt. Mit der Methode select können Sie festlegen, eines Inhaltselements. Sie können auch mehrere Inhalte Elemente.

Angenommen, Sie haben ein Dokument, das Folgendes enthält:

<div id="nameTag">
  <div class="first">Bob</div>
  <div>B. Love</div>
  <div class="email">bob@</div>
</div>

und einen Schattenstamm, der CSS-Selektoren verwendet, um bestimmte Inhalte auszuwählen:

<div style="background: purple; padding: 1em;">
  <div style="color: red;">
    <content **select=".first"**></content>
  </div>
  <div style="color: yellow;">
    <content **select="div"**></content>
  </div>
  <div style="color: blue;">
    <content **select=".email">**</content>
  </div>
</div>

Das <div class="email">-Element stimmt mit beiden überein <content select="div">- und <content select=".email">-Elemente. Wie oft hat Benjamins E-Mail und in welchen Farben?

Die Antwort ist, dass Bobs E-Mail-Adresse einmal angezeigt wird und gelb ist.

Nutzer, die Shadow DOM hacken, wissen nämlich, Die Konstruktion des Baums dessen, was tatsächlich auf dem Bildschirm gerendert wird, ist wie eine eine große Party. Das Inhaltselement ist die Einladung, Inhalte aus dem Dokument in das Backstage-Schatten-DOM-Rendering Diese Einladungen werden der Reihe nach zugestellt: für wen hängt davon ab, an wen sie gerichtet ist (d. h. Das Attribut select.) Inhalte einmalig nimmt die Einladung immer an (wer würde das nicht?) geht. Wenn eine nachfolgende Einladung wieder an diese Adresse gesendet wird, niemand zu Hause ist und es auch nicht zu euch kommt.

Im obigen Beispiel stimmt <div class="email"> mit die div-Auswahl und die .email Da das Inhaltselement mit dem div-Element Selector weiter oben im Dokument, <div class="email"> geht zur gelben Party und dass niemand zur blauen Party kommt. (Das könnte Warum ist es so blau, obwohl Elend die Gesellschaft liebt, nie wissen.)

Wenn etwas zu keinen Partys eingeladen wird, wird es nicht berücksichtigt, überhaupt gerendert werden. Genau das ist mit dem Text „Hello, world“ in aus dem allerersten Beispiel. Dies ist nützlich, wenn Sie eine völlig anderes Rendering: Schreiben Sie das semantische Modell Dokument, auf das Skripts auf der Seite zugreifen können, aber das Dokument zu Rendering-Zwecken verwenden und mit einem ganz anderen mit JavaScript in Shadow DOM gerendert wird.

HTML hat beispielsweise eine schöne Datumsauswahl. Wenn Sie <input type="date"> schreiben, erhalten Sie einen übersichtlichen Pop-up-Kalender. Aber was ist, wenn Sie Der Nutzer soll einen Zeitraum für sein Dessert auswählen können. Inselurlaub (mit Hängematten aus Red Vines) Ich Richten Sie Ihr Dokument so ein:

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

erstellen Sie ein Shadow DOM, das anhand einer Tabelle einen schnellen Kalender erstellt. in dem der Zeitraum hervorgehoben wird usw. Wenn Nutzende auf der Tage im Kalender aktualisiert, aktualisiert die Komponente den Status im StartDate- und endDate-Eingaben; wenn der Nutzer das Formular absendet, werden die Werte dieser Eingabeelemente gesendet.

Warum habe ich Labels in das Dokument eingefügt, gerendert? Wenn ein Nutzer das Formular in einem Browser aufruft, das Shadow DOM nicht unterstützt, ist das Formular trotzdem nutzbar, nur nicht so sehr hübsch. Der Nutzer sieht etwa Folgendes:

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

Du übergehst Shadow DOM 101

Das sind die Grundlagen von Shadow DOM – du übergibst Shadow DOM 101! Sie können Mehr Möglichkeiten mit Shadow DOM. Sie können z. B. mehrere Schatten auf Ein Schattenhost oder verschachtelte Schatten für die Kapselung oder Architektur mit Model-Driven Views (MDV) und Shadow DOM. Und Web Komponenten sind mehr als nur Shadow DOM.

Wir erklären dies in späteren Posts.