Podstawy DOM – Shadow

Dominic Cooney
Dominic Cooney

Wprowadzenie

Web Komponenty to zestaw najnowszych standardów, które:

  1. Umożliwiają tworzenie widżetów
  2. ...które można z łatwością ponownie wykorzystać
  3. ...które nie spowodują uszkodzenia stron, jeśli nowa wersja komponentu zmienia wewnętrzne szczegóły implementacji.

Czy to oznacza, że musisz zdecydować, kiedy używać HTML/JavaScriptu? Kiedy używać komponentów sieciowych? Nie! HTML i JavaScript pozwalają interaktywne treści wizualne. Widżety to interaktywne elementy wizualne. it warto wykorzystać umiejętności HTML i JavaScript podczas aby móc tworzyć widżety. Standardy komponentów sieciowych mają pomagać i to w niej.

Istnieje jednak zasadniczy problem, który sprawia, że widżety są oparte na Trudne w obsłudze HTML i JavaScript: drzewo DOM w widżecie są oddzielone od reszty strony. Ten brak ujęć oznacza, że arkusz stylów dokumentu może zostać przypadkowo zastosowany do części wewnątrz widżetu, Twój kod JavaScript może przypadkowo zmodyfikować części wewnątrz widżetu, identyfikatory mogą się pokrywać z identyfikatorami wewnątrz widżetu; i tak dalej.

Komponenty sieciowe składają się z 3 części:

  1. Szablony
  2. Shadow DOM
  3. Elementy niestandardowe

Shadow DOM rozwiązuje problem z enkapulacją drzewa DOM. 4 części komponentów sieciowych zaprojektowano tak, by ze sobą współdziałały, ale może też wskazać, które części komponentów sieciowych mają być używane. Ten pokazuje, jak korzystać z modelu Shadow DOM.

Cześć, świecie cieni

Dzięki modelowi Shadow DOM elementy mogą otrzymywać nowy rodzaj węzła powiązany z . Ten nowy rodzaj węzła jest nazywany główną cieniem. Element, z którym powiązany jest pierwiastek cienia, jest nazywany cieniem. i hostingu. zawartość hosta w cieniu nie jest renderowana; treść a zamiast tego renderowany jest pierwiastek cienia.

Jeśli na przykład masz znaczniki podobne do tych:

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

to zamiast

<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>

jak wygląda twoja strona

<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>

Co więcej, jeśli JavaScript na stronie pyta o przycisk textContent to, nie uzyska „こんんちちす影の世界!”, ale „Hello, world!” bo poddrzewo DOM pod pierwiastek cienia.

Oddzielenie treści od prezentacji

Teraz przyjrzymy się użyciu modelu Shadow DOM do oddzielania treści prezentacji. Załóżmy, że mamy taki 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>

Oto znaczniki. Tak można by napisać dzisiaj. Nie użyj modelu Shadow DOM:

<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>

Drzewo DOM nie zawiera hermetyzacji, dlatego cała struktura tag jest widoczny dla dokumentu. Jeśli inne elementy na stronie przypadki użycia tych samych nazw klas w stylach lub skryptach, będziemy mieć kiepską atmosferę.

Możemy uniknąć problemów.

Krok 1. Ukryj szczegóły prezentacji

Z punktu widzenia semantyki zależy nam tylko na tym, aby:

  • To plakietka.
  • Masz na imię „Bob”.

Najpierw tworzymy znaczniki, które są bliższe prawdziwie wymaganej semantyce:

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

Następnie umieść wszystkie style i elementy div użyte w prezentacji element <template>:

<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>

W tym momencie wyświetlany jest tylko fragment „Robert”. Ponieważ przenieśliśmy prezentacyjne elementy DOM wewnątrz <template>, nie są renderowane, można uzyskać do nich dostęp z JavaScriptu. Teraz robimy to, by wypełnij pierwiastek cienia:

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

Po skonfigurowaniu rzędu rzędu cienia tag jest renderowany ponownie. Jeśli klikniesz tag prawym przyciskiem myszy i sprawdzisz który jest słodkim, semantycznym znacznikiem:

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

To pokazuje, że za pomocą modelu Shadow DOM ukryliśmy szczegóły prezentacji tagu nazwy z dokumentu. szczegóły prezentacji są zawarte w modelu Shadow DOM.

Krok 2. Oddziel treść od prezentacji

Nasz tag nazwy ukrywa teraz szczegóły prezentacji na stronie, ale nie oddziela prezentacji od treści, treść (nazwa „Bob”) jest na stronie, wyświetlana nazwa; który skopiowaliśmy do rdzenia cienia. Jeśli chcemy zmienić parametr nazwy na plakietce, musimy to zrobić w dwóch miejscach, nie są zsynchronizowane.

Elementy HTML są kompozycyjne – możesz umieścić przycisk wewnątrz tabeli, na przykład. Potrzebujemy tu kompozycji: tagu nazwy musi być kompozycja czerwonego tła: tekst i zawartość który znajduje się na plakietce.

Jako autor komponentu określasz sposób działania kompozycji za pomocą nowego elementu o nazwie <content>. Ten tworzy punkt wstawiania w prezentacji widżetu, a element punkt wstawiania wybiera treść z hosta-cienia w celu przedstawienia w danym momencie.

Jeśli zmienimy znaczniki w modelu Shadow DOM na takie:

<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>

Podczas renderowania tagu zawartość hosta cienia jest jest rzutowana na miejsce, w którym element <content>

Struktura dokumentu jest prostsza, ponieważ nazwa to dokument jest dostępny w jednym miejscu. Jeśli kiedykolwiek zechcesz zaktualizować tag nazwę użytkownika, wystarczy napisać:

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

To wszystko. Renderowanie tagu jest automatycznie aktualizowane przez przeglądarkę, ponieważ rzutujemy zawartość tag <content>.

<div id="ex2b">

Teraz osiągnęliśmy oddzielenie treści od sposobu prezentacji. jaka treść znajduje się w dokumencie; prezentacja jest w modelu Shadow DOM. Są automatycznie synchronizowane przez przeglądarkę na bieżąco do renderowania.

Krok 3: Zysk

Rozdzielając treść i prezentację, możemy uprościć który modyfikuje treść (w przykładowym tagu nazw), zajmuje jedynie prostą strukturę zawierającą jeden <div> zamiast kilku.

Jeśli zmienimy prezentację, nie będziemy musieli zmieniać !

Załóżmy na przykład, że chcemy zlokalizować tag. To nadal nazwa , aby semantyczna zawartość dokumentu nie uległa zmianie:

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

Kod konfiguracji shadow root pozostaje bez zmian. Tylko to, co trafia do zmiany pierwiastka cienia:

<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>

To ogromna poprawa w stosunku do obecnej sytuacji w internecie, kod aktualizacji nazwy może zależeć od struktury komponentu, który jest prosty i spójny. Imię i nazwisko do aktualizacji kodu nie trzeba znać struktury wykorzystywanej renderowanie. Jeśli przyjrzymy się renderowaniu, nazwa pojawi się drugą po angielsku (po „Hi! Mam na imię”), ale najpierw po japońsku. (przed „조申すます”). To rozróżnienie jest semantyczne bez znaczenia z myślą o aktualizacji wyświetlanej nazwy, więc kod aktualizacji nazwy nie musi znać tych szczegółów.

Dodatkowy zasługa: Projekcja zaawansowana

W powyższym przykładzie element <content> wybiera całą zawartość z hosta-cienia. Za pomocą select, możesz określić, projektów z elementami treści. Możesz też dodawać wiele treści .

Jeśli na przykład masz dokument, który zawiera:

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

i pierwiastek cienia, który za pomocą selektorów CSS wybiera konkretną treść:

<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>

Element <div class="email"> pasuje do obu elementów <content select="div"> i <content select=".email">. Ile razy pisze e-mail Roberta i w jakich kolorach ?

Chodzi o to, że adres e-mail Roberta pojawia się raz i jest żółty.

Ponieważ, jak wiedzą osoby hakerskie Shadow DOM, Tworzenie drzewa tego, co jest rzeczywiście renderowane na ekranie, wielkiej imprezie. Element content to zaproszenie, z dokumentu do zakulisowego renderowania Shadow DOM. stronie. Zaproszenia są dostarczane po kolei. kto dostaje zależy od tego, do kogo jest kierowane (tzn. atrybut select). Content, raz zaproszony, zawsze akceptuje zaproszenie (kto by tego nie chciał?!) i rezygnuje z niego. idzie. Jeśli kolejne zaproszenie zostanie ponownie wysłane na ten adres, cóż, Nikogo nie ma w domu i nie chodzi o Twoją imprezę.

W powyższym przykładzie <div class="email"> wskazuje dopasowanie do zarówno selektor div, jak i .email selektora, ale ponieważ element content z tagiem div jest dostępna wcześniej w dokumencie, <div class="email"> trafia na żółtą drużynę, nikt nie może przyjść na niebieski klub. (Może to być dlaczego jest tak niebieski, chociaż niedość kocha towarzystwo. nigdy nie wiadomo).

Jeśli coś zostanie zaproszone na żadne grupy, to nie zostanie. nie wyrenderowano w ogóle. Tak właśnie stało się z tekstem „Hello, world” z pierwszego przykładu. Jest to przydatne, gdy chcesz uzyskać radykalnie odmienne renderowanie. Napisz model semantyczny w który jest dostępny dla skryptów na stronie, ale ukryj w celu renderowania i łączenie jej z do renderowania w modelu Shadow DOM za pomocą JavaScriptu.

Na przykład HTML ma wygodny selektor daty. Jeśli napiszesz <input type="date">, otrzymasz przejrzysty kalendarz w wyskakującym okienku. Ale co jeśli chcesz, aby użytkownik mógł wybrać zakres dat deseru wakacje na wyspie (pewnie... z hamakami z czerwonych winorośli) Ty skonfiguruj dokument w następujący sposób:

<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>

ale należy utworzyć model Shadow DOM, który korzysta z tabeli i tworzy zgrabny kalendarz. który podświetla zakres dat itd. Gdy użytkownik kliknie dni w kalendarzu, komponent aktualizuje stan w elemencie dane wejściowe startDate i endDate; gdy użytkownik przesyła formularz, są przesyłane wartości z tych elementów.

Po co uwzględniłem w dokumencie etykiety, skoro nie będą? wyrenderowano? Dzieje się tak, ponieważ jeśli użytkownik wyświetla formularz w przeglądarce, który nie obsługuje modelu Shadow DOM, formularz jest wciąż przydatny. ładny. Użytkownik widzi coś takiego:

<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>

You Pass Shadow DOM 101

To podstawy Shadow DOM — z reguły Shadow DOM 101. Dostępne opcje więcej możliwości Shadow DOM. Możesz na przykład stosować wiele cieni jednego hosta cieni, zagnieżdżonych cieni do zastosowania lub architektury architektonicznej. za pomocą widoków opartych na modelach (MDV) i Shadow DOM. I w przeglądarce Komponenty to więcej niż tylko model Shadow DOM.

W dalszych postach omówimy te kwestie.