Введение
Веб-компоненты — это набор передовых стандартов, которые:
- Сделайте возможным создание виджетов
- …которые можно надежно использовать повторно
- …и который не повредит страницы, если следующая версия компонента изменит детали внутренней реализации.
Означает ли это, что вам придется решать, когда использовать HTML/JavaScript, а когда веб-компоненты? Нет! HTML и JavaScript могут создавать интерактивные визуальные материалы. Виджеты — это интерактивные визуальные элементы. При разработке виджета имеет смысл использовать свои навыки HTML и JavaScript. Стандарты веб-компонентов призваны помочь вам в этом.
Но есть фундаментальная проблема, которая затрудняет использование виджетов, созданных на основе HTML и JavaScript: дерево DOM внутри виджета не инкапсулировано из остальной части страницы. Отсутствие инкапсуляции означает, что таблица стилей вашего документа может случайно примениться к частям внутри виджета; ваш JavaScript может случайно изменить части внутри виджета; ваши идентификаторы могут пересекаться с идентификаторами внутри виджета; и так далее.
Веб-компоненты состоят из трех частей:
Shadow DOM решает проблему инкапсуляции дерева DOM. Четыре части веб-компонентов предназначены для совместной работы, но вы также можете выбирать, какие части веб-компонентов использовать. В этом руководстве показано, как использовать Shadow DOM.
Привет, мир теней
С помощью Shadow DOM элементы могут получить связанный с ними узел нового типа. Этот новый тип узла называется теневым корнем . Элемент, с которым связан теневой корень, называется теневым хостом. Содержимое теневого хоста не отображается; вместо этого отображается содержимое теневого корня.
Например, если у вас была такая разметка:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
тогда вместо
<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>
ваша страница выглядит так
<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>
Мало того, если JavaScript на странице запрашивает textContent
кнопки, он получит не «こんにちは、影の世界!», а «Hello, world!» потому что поддерево DOM под теневым корнем инкапсулировано.
Отделение контента от презентации
Теперь мы рассмотрим использование Shadow DOM для отделения контента от представления. Допустим, у нас есть этот тег имени:
<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>
Вот разметка. Это то, что вы бы написали сегодня. Он не использует 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>
Поскольку в дереве DOM отсутствует инкапсуляция, документу доступна вся структура тега имени. Если другие элементы на странице случайно используют те же имена классов для стилизации или написания сценариев, нам придется плохо.
Мы можем избежать неприятностей.
Шаг 1. Скройте детали презентации
Семантически нас, вероятно, волнует только то, что:
- Это именной тег.
- Имя «Боб».
Сначала мы пишем разметку, которая ближе к истинной семантике, которую мы хотим:
<div id="nameTag">Bob</div>
Затем мы помещаем все стили и элементы div, используемые для представления, в элемент <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>
На данный момент «Боб» — единственное, что визуализируется. Поскольку мы переместили презентационные элементы DOM внутрь элемента <template>
, они не отображаются, но к ним можно получить доступ из JavaScript. Мы делаем это сейчас, чтобы заполнить теневой корень:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Теперь, когда мы настроили теневой корень, тег имени отображается снова. Если вы щелкнете правой кнопкой мыши по тегу имени и осмотрите элемент, вы увидите, что это приятная семантическая разметка:
<div id="nameTag">Bob</div>
Это демонстрирует, что с помощью Shadow DOM мы скрыли детали представления тега имени из документа. Детали презентации инкапсулированы в Shadow DOM.
Шаг 2. Отделите контент от презентации
Наш тег имени теперь скрывает детали презентации со страницы, но на самом деле он не отделяет презентацию от контента, поскольку, хотя контент (имя «Боб») находится на странице, отображаемое имя — это то, которое мы скопировали в теневой корень. Если мы хотим изменить имя в теге имени, нам придется сделать это в двух местах, и они могут не синхронизироваться.
Элементы HTML являются композиционными — например, вы можете поместить кнопку внутри таблицы. Здесь нам нужна композиция: бейдж с именем должен представлять собой композицию на красном фоне с надписью «Привет!» текст и содержимое тега имени.
Вы, автор компонента, определяете, как композиция работает с вашим виджетом, используя новый элемент под названием <content>
. Это создает точку вставки в представлении виджета, и эта точка вставки выбирает содержимое теневого хоста для представления в этой точке.
Если мы изменим разметку в Shadow DOM на такую:
<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>
При отображении тега имени содержимое теневого хоста проецируется в то место, где появляется элемент <content>
.
Теперь структура документа стала проще, поскольку имя находится только в одном месте — документе. Если вашей странице когда-либо понадобится обновить имя пользователя, просто напишите:
document.querySelector('#nameTag').textContent = 'Shellie';
и это все. Отображение тега имени автоматически обновляется браузером, поскольку мы проецируем содержимое тега имени на место с помощью <content>
.
<div id="ex2b">
Теперь мы добились разделения контента и подачи. Содержимое находится в документе; презентация находится в Shadow DOM. Они автоматически синхронизируются браузером, когда приходит время что-то визуализировать.
Шаг 3: Прибыль
Разделив контент и представление, мы можем упростить код, который манипулирует контентом — в примере с тегом имени этому коду нужно иметь дело только с простой структурой, содержащей один <div>
вместо нескольких.
Теперь, если мы изменим нашу презентацию, нам не нужно будет менять какой-либо код!
Например, предположим, что мы хотим локализовать наш тег имени. Это по-прежнему тег имени, поэтому смысловое содержание документа не меняется:
<div id="nameTag">Bob</div>
Код установки теневого корня остается прежним. Изменяется только то, что помещается в теневой корень:
<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>
Это большое улучшение по сравнению с сегодняшней ситуацией в Интернете, поскольку ваш код обновления имени может зависеть от структуры компонента , которая является простой и последовательной. Коду обновления имени не обязательно знать структуру, используемую для рендеринга. Если мы посмотрим на то, что отображается, имя появится вторым на английском языке (после «Привет! Меня зовут»), но первым на японском (перед «と申します»). Это различие семантически бессмысленно с точки зрения обновления отображаемого имени, поэтому коду обновления имени не обязательно знать об этой детали.
Дополнительный балл: Продвинутая проекция
В приведенном выше примере элемент <content>
выбирает весь контент с теневого хоста. Используя атрибут select
, вы можете контролировать, что проецирует элемент контента. Вы также можете использовать несколько элементов контента.
Например, если у вас есть документ, который содержит следующее:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
и теневой корень, который использует селекторы CSS для выбора определенного контента:
<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>
Элемент <div class="email">
соответствует элементам <content select="div">
и <content select=".email">
. Сколько раз появляется адрес электронной почты Боба и какими цветами?
Ответ заключается в том, что адрес электронной почты Боба появляется один раз и имеет желтый цвет.
Причина в том, что, как знают люди, взламывающие Shadow DOM, построение дерева того, что на самом деле отображается на экране, похоже на огромную вечеринку. Элемент контента — это приглашение, которое позволяет контенту из документа попасть в закулисную группу рендеринга Shadow DOM. Эти приглашения доставляются по порядку; кто получит приглашение, зависит от того, кому оно адресовано (то есть атрибут select
). Содержимое, однажды приглашенное, всегда принимает приглашение (кто бы не стал?!) и уходит. Если последующее приглашение будет отправлено на этот адрес еще раз, что ж, никого нет дома, и оно не придет на вашу вечеринку.
В приведенном выше примере <div class="email">
соответствует как селектору div
, так и селектору .email
, но поскольку элемент контента с селектором div
находится в документе раньше, <div class="email">
переходит к желтой стороне, и никто не может перейти к синей стороне. (Возможно, поэтому он такой синий, хотя несчастье любит компанию, так что никогда не знаешь.)
Если что-то не приглашено ни на одну из сторон, то оно вообще не будет визуализировано. Именно это произошло с текстом «Привет, мир» в самом первом примере. Это полезно, когда вы хотите добиться радикально иного рендеринга: напишите в документе семантическую модель, которая доступна для скриптов на странице, но скройте ее для целей рендеринга и подключите ее к действительно другой модели рендеринга в Shadow DOM с помощью JavaScript.
Например, в HTML есть удобный инструмент выбора даты. Если вы напишете <input type="date">
вы получите аккуратный всплывающий календарь. Но что, если вы хотите, чтобы пользователь мог выбрать диапазон дат для отпуска на десертном острове (вы знаете… с гамаками, сделанными из красных лоз). Вы настраиваете свой документ следующим образом:
<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>
но создайте Shadow DOM, который использует таблицу для создания удобного календаря, в котором выделяется диапазон дат и так далее. Когда пользователь щелкает дни в календаре, компонент обновляет состояние входных данных startDate и endDate; когда пользователь отправляет форму, значения из этих элементов ввода отправляются.
Зачем я включил в документ метки, если они не будут отображаться? Причина в том, что если пользователь просматривает форму в браузере, который не поддерживает Shadow DOM, форму по-прежнему можно использовать, но не так красиво. Пользователь видит что-то вроде:
<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>
Вы проходите тень DOM 101
Это основы Shadow DOM — вы проходите Shadow DOM 101! Вы можете делать больше с помощью Shadow DOM, например, вы можете использовать несколько теней на одном теневом хосте или вложенные тени для инкапсуляции, или спроектировать свою страницу с помощью представлений на основе модели (MDV) и Shadow DOM. Веб-компоненты — это больше, чем просто Shadow DOM.
Мы объясним это в последующих постах.
,Введение
Веб-компоненты — это набор передовых стандартов, которые:
- Сделайте возможным создание виджетов
- …которые можно надежно использовать повторно
- …и который не повредит страницы, если следующая версия компонента изменит детали внутренней реализации.
Означает ли это, что вам придется решать, когда использовать HTML/JavaScript, а когда веб-компоненты? Нет! HTML и JavaScript могут создавать интерактивные визуальные материалы. Виджеты — это интерактивные визуальные элементы. При разработке виджета имеет смысл использовать свои навыки HTML и JavaScript. Стандарты веб-компонентов призваны помочь вам в этом.
Но есть фундаментальная проблема, которая затрудняет использование виджетов, созданных на основе HTML и JavaScript: дерево DOM внутри виджета не инкапсулировано из остальной части страницы. Отсутствие инкапсуляции означает, что таблица стилей вашего документа может случайно примениться к частям внутри виджета; ваш JavaScript может случайно изменить части внутри виджета; ваши идентификаторы могут пересекаться с идентификаторами внутри виджета; и так далее.
Веб-компоненты состоят из трех частей:
Shadow DOM решает проблему инкапсуляции дерева DOM. Четыре части веб-компонентов предназначены для совместной работы, но вы также можете выбирать, какие части веб-компонентов использовать. В этом руководстве показано, как использовать Shadow DOM.
Привет, мир теней
С помощью Shadow DOM элементы могут получить связанный с ними узел нового типа. Этот новый тип узла называется теневым корнем . Элемент, с которым связан теневой корень, называется теневым хостом. Содержимое теневого хоста не отображается; вместо этого отображается содержимое теневого корня.
Например, если у вас была такая разметка:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
тогда вместо
<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>
ваша страница выглядит так
<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>
Мало того, если JavaScript на странице запрашивает textContent
кнопки, он получит не «こんにちは、影の世界!», а «Hello, world!» потому что поддерево DOM под теневым корнем инкапсулировано.
Отделение контента от презентации
Теперь мы рассмотрим использование Shadow DOM для отделения контента от представления. Допустим, у нас есть этот тег имени:
<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>
Вот разметка. Это то, что вы бы написали сегодня. Он не использует 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>
Поскольку в дереве DOM отсутствует инкапсуляция, документу доступна вся структура тега имени. Если другие элементы на странице случайно используют те же имена классов для стилизации или написания сценариев, нам придется плохо.
Мы можем избежать неприятностей.
Шаг 1. Скройте детали презентации
Семантически нас, вероятно, волнует только то, что:
- Это именной тег.
- Имя «Боб».
Сначала мы пишем разметку, которая ближе к истинной семантике, которую мы хотим:
<div id="nameTag">Bob</div>
Затем мы помещаем все стили и элементы div, используемые для представления, в элемент <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>
На данный момент «Боб» — единственное, что визуализируется. Поскольку мы переместили презентационные элементы DOM внутрь элемента <template>
, они не отображаются, но к ним можно получить доступ из JavaScript. Мы делаем это сейчас, чтобы заполнить теневой корень:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Теперь, когда мы настроили теневой корень, тег имени отображается снова. Если вы щелкнете правой кнопкой мыши по тегу имени и осмотрите элемент, вы увидите, что это приятная семантическая разметка:
<div id="nameTag">Bob</div>
Это демонстрирует, что с помощью Shadow DOM мы скрыли детали представления тега имени из документа. Детали презентации инкапсулированы в Shadow DOM.
Шаг 2. Отделите контент от презентации
Наш тег имени теперь скрывает детали презентации со страницы, но на самом деле он не отделяет презентацию от контента, поскольку, хотя контент (имя «Боб») находится на странице, отображаемое имя — это то, которое мы скопировали в теневой корень. Если мы хотим изменить имя в теге имени, нам придется сделать это в двух местах, и они могут не синхронизироваться.
Элементы HTML являются композиционными — например, вы можете поместить кнопку внутри таблицы. Здесь нам нужна композиция: бейдж с именем должен представлять собой композицию на красном фоне с надписью «Привет!» текст и содержимое тега имени.
Вы, автор компонента, определяете, как композиция работает с вашим виджетом, используя новый элемент под названием <content>
. Это создает точку вставки в представлении виджета, и эта точка вставки выбирает содержимое теневого хоста для представления в этой точке.
Если мы изменим разметку в Shadow DOM на такую:
<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>
При отображении тега имени содержимое теневого хоста проецируется в то место, где появляется элемент <content>
.
Теперь структура документа стала проще, поскольку имя находится только в одном месте — документе. Если вашей странице когда-либо понадобится обновить имя пользователя, просто напишите:
document.querySelector('#nameTag').textContent = 'Shellie';
и это все. Отображение тега имени автоматически обновляется браузером, поскольку мы проецируем содержимое тега имени на место с помощью <content>
.
<div id="ex2b">
Теперь мы добились разделения контента и подачи. Содержимое находится в документе; презентация находится в Shadow DOM. Они автоматически синхронизируются браузером, когда приходит время что-то визуализировать.
Шаг 3: Прибыль
Разделив контент и представление, мы можем упростить код, который манипулирует контентом — в примере с тегом имени этому коду нужно иметь дело только с простой структурой, содержащей один <div>
вместо нескольких.
Теперь, если мы изменим нашу презентацию, нам не нужно будет менять какой-либо код!
Например, предположим, что мы хотим локализовать наш тег имени. Это по-прежнему тег имени, поэтому смысловое содержание документа не меняется:
<div id="nameTag">Bob</div>
Код установки теневого корня остается прежним. Изменяется только то, что помещается в теневой корень:
<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>
Это большое улучшение по сравнению с сегодняшней ситуацией в Интернете, поскольку ваш код обновления имени может зависеть от структуры компонента , которая является простой и последовательной. Коду обновления имени не обязательно знать структуру, используемую для рендеринга. Если мы посмотрим на то, что отображается, имя появится вторым на английском языке (после «Привет! Меня зовут»), но первым на японском (перед «と申します»). Это различие семантически бессмысленно с точки зрения обновления отображаемого имени, поэтому коду обновления имени не обязательно знать об этой детали.
Дополнительный балл: Продвинутая проекция
В приведенном выше примере элемент <content>
выбирает весь контент с теневого хоста. Используя атрибут select
, вы можете контролировать, что проецирует элемент контента. Вы также можете использовать несколько элементов контента.
Например, если у вас есть документ, который содержит следующее:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
и теневой корень, который использует селекторы CSS для выбора определенного контента:
<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>
Элемент <div class="email">
соответствует элементам <content select="div">
и <content select=".email">
. Сколько раз появляется адрес электронной почты Боба и какими цветами?
Ответ заключается в том, что адрес электронной почты Боба появляется один раз и имеет желтый цвет.
Причина в том, что, как знают люди, взламывающие Shadow DOM, построение дерева того, что на самом деле отображается на экране, похоже на огромную вечеринку. Элемент контента — это приглашение, которое позволяет контенту из документа попасть в закулисную группу рендеринга Shadow DOM. Эти приглашения доставляются по порядку; кто получит приглашение, зависит от того, кому оно адресовано (то есть атрибут select
). Содержимое, однажды приглашенное, всегда принимает приглашение (кто бы не стал?!) и уходит. Если последующее приглашение будет отправлено на этот адрес еще раз, что ж, никого нет дома, и оно не придет на вашу вечеринку.
В приведенном выше примере <div class="email">
соответствует как селектору div
, так и селектору .email
, но поскольку элемент содержимого с селектором div
находится в документе раньше, <div class="email">
переходит к желтой стороне, и никто не может перейти к синей стороне. (Возможно, поэтому он такой синий, хотя несчастье любит компанию, так что никогда не знаешь.)
Если что-то не приглашено ни на одну из сторон, то оно вообще не будет визуализировано. Именно это произошло с текстом «Привет, мир» в самом первом примере. Это полезно, когда вы хотите добиться радикально иного рендеринга: напишите в документе семантическую модель, которая доступна для скриптов на странице, но скройте ее для целей рендеринга и подключите ее к действительно другой модели рендеринга в Shadow DOM с помощью JavaScript.
Например, в HTML есть удобный инструмент выбора даты. Если вы напишете <input type="date">
вы получите аккуратный всплывающий календарь. Но что, если вы хотите, чтобы пользователь мог выбрать диапазон дат для отпуска на десертном острове (вы знаете… с гамаками, сделанными из красных лоз). Вы настраиваете свой документ следующим образом:
<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>
но создайте Shadow DOM, который использует таблицу для создания удобного календаря, в котором выделяется диапазон дат и так далее. Когда пользователь щелкает дни в календаре, компонент обновляет состояние входных данных startDate и endDate; когда пользователь отправляет форму, значения из этих элементов ввода отправляются.
Зачем я включил в документ метки, если они не будут отображаться? Причина в том, что если пользователь просматривает форму в браузере, который не поддерживает Shadow DOM, форму по-прежнему можно использовать, но не так красиво. Пользователь видит что-то вроде:
<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>
Вы проходите тень DOM 101
Это основы Shadow DOM — вы проходите Shadow DOM 101! С помощью Shadow DOM вы можете делать больше, например, вы можете использовать несколько теней на одном теневом хосте или вложенные тени для инкапсуляции, или спроектировать свою страницу с помощью представлений на основе модели (MDV) и Shadow DOM. Веб-компоненты — это больше, чем просто Shadow DOM.
Мы объясним это в последующих постах.
,Введение
Веб-компоненты — это набор передовых стандартов, которые:
- Сделайте возможным создание виджетов
- …которые можно надежно использовать повторно
- …и который не повредит страницы, если следующая версия компонента изменит детали внутренней реализации.
Означает ли это, что вам придется решать, когда использовать HTML/JavaScript, а когда веб-компоненты? Нет! HTML и JavaScript могут создавать интерактивные визуальные материалы. Виджеты — это интерактивные визуальные элементы. При разработке виджета имеет смысл использовать свои навыки HTML и JavaScript. Стандарты веб-компонентов призваны помочь вам в этом.
Но есть фундаментальная проблема, которая затрудняет использование виджетов, созданных на основе HTML и JavaScript: дерево DOM внутри виджета не инкапсулировано из остальной части страницы. Отсутствие инкапсуляции означает, что таблица стилей вашего документа может случайно примениться к частям внутри виджета; ваш JavaScript может случайно изменить части внутри виджета; ваши идентификаторы могут пересекаться с идентификаторами внутри виджета; и так далее.
Веб-компоненты состоят из трех частей:
Shadow DOM решает проблему инкапсуляции дерева DOM. Четыре части веб-компонентов предназначены для совместной работы, но вы также можете выбирать, какие части веб-компонентов использовать. В этом руководстве показано, как использовать Shadow DOM.
Привет, мир теней
С помощью Shadow DOM элементы могут получить связанный с ними узел нового типа. Этот новый тип узла называется теневым корнем . Элемент, с которым связан теневой корень, называется теневым хостом. Содержимое теневого хоста не отображается; вместо этого отображается содержимое теневого корня.
Например, если у вас была такая разметка:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
тогда вместо
<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>
ваша страница выглядит так
<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>
Мало того, если JavaScript на странице запрашивает textContent
кнопки, он получит не «こんにちは、影の世界!», а «Hello, world!» потому что поддерево DOM под теневым корнем инкапсулировано.
Отделение контента от презентации
Теперь мы рассмотрим использование Shadow DOM для отделения контента от представления. Допустим, у нас есть этот тег имени:
<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>
Вот разметка. Это то, что вы бы написали сегодня. Он не использует 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>
Поскольку в дереве DOM отсутствует инкапсуляция, документу доступна вся структура тега имени. Если другие элементы на странице случайно используют те же имена классов для стилизации или написания сценариев, нам придется плохо.
Мы можем избежать неприятностей.
Шаг 1. Скройте детали презентации
Семантически нас, вероятно, волнует только то, что:
- Это именной тег.
- Имя «Боб».
Сначала мы пишем разметку, которая ближе к истинной семантике, которую мы хотим:
<div id="nameTag">Bob</div>
Затем мы помещаем все стили и элементы div, используемые для представления, в элемент <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>
На данный момент «Боб» — единственное, что визуализируется. Поскольку мы переместили презентационные элементы DOM внутрь элемента <template>
, они не отображаются, но к ним можно получить доступ из JavaScript. Мы делаем это сейчас, чтобы заполнить теневой корень:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Теперь, когда мы настроили теневой корень, тег имени отображается снова. Если вы щелкнете правой кнопкой мыши по тегу имени и осмотрите элемент, вы увидите, что это приятная семантическая разметка:
<div id="nameTag">Bob</div>
Это демонстрирует, что с помощью Shadow DOM мы скрыли детали представления тега имени из документа. Детали презентации инкапсулированы в Shadow DOM.
Шаг 2. Отделите контент от презентации
Наш тег имени теперь скрывает детали презентации со страницы, но на самом деле он не отделяет презентацию от контента, поскольку, хотя контент (имя «Боб») находится на странице, отображаемое имя — это то, которое мы скопировали в теневой корень. Если мы хотим изменить имя в теге имени, нам придется сделать это в двух местах, и они могут не синхронизироваться.
Элементы HTML являются композиционными — например, вы можете поместить кнопку внутри таблицы. Здесь нам нужна композиция: бейдж с именем должен представлять собой композицию на красном фоне с надписью «Привет!» текст и содержимое тега имени.
Вы, автор компонента, определяете, как композиция работает с вашим виджетом, используя новый элемент под названием <content>
. Это создает точку вставки в представлении виджета, и эта точка вставки выбирает содержимое теневого хоста для представления в этой точке.
Если мы изменим разметку в Shadow DOM на такую:
<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>
При отображении тега имени содержимое теневого хоста проецируется в то место, где появляется элемент <content>
.
Теперь структура документа стала проще, поскольку имя находится только в одном месте — документе. Если вашей странице когда-либо понадобится обновить имя пользователя, просто напишите:
document.querySelector('#nameTag').textContent = 'Shellie';
и это все. Отображение тега имени автоматически обновляется браузером, поскольку мы проецируем содержимое тега имени на место с помощью <content>
.
<div id="ex2b">
Теперь мы добились разделения контента и представления. Содержимое находится в документе; презентация находится в Shadow DOM. Они автоматически синхронизируются браузером, когда приходит время что-то визуализировать.
Шаг 3: Прибыль
Разделив контент и представление, мы можем упростить код, который манипулирует контентом — в примере с тегом имени этому коду нужно иметь дело только с простой структурой, содержащей один <div>
вместо нескольких.
Теперь, если мы изменим нашу презентацию, нам не нужно будет менять какой-либо код!
Например, предположим, что мы хотим локализовать наш тег имени. Это по-прежнему тег имени, поэтому смысловое содержание документа не меняется:
<div id="nameTag">Bob</div>
Код установки теневого корня остается прежним. Изменяется только то, что помещается в теневой корень:
<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>
Это большое улучшение по сравнению с сегодняшней ситуацией в Интернете, поскольку ваш код обновления имени может зависеть от структуры компонента , которая является простой и последовательной. Коду обновления имени не обязательно знать структуру, используемую для рендеринга. Если мы посмотрим на то, что отображается, имя появится вторым на английском языке (после «Привет! Меня зовут»), но первым на японском (перед «と申します»). Это различие семантически бессмысленно с точки зрения обновления отображаемого имени, поэтому коду обновления имени не обязательно знать об этой детали.
Дополнительный балл: Продвинутая проекция
В приведенном выше примере элемент <content>
выбирает весь контент с теневого хоста. Используя атрибут select
, вы можете контролировать, что проецирует элемент контента. Вы также можете использовать несколько элементов контента.
Например, если у вас есть документ, который содержит следующее:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
и теневой корень, который использует селекторы CSS для выбора определенного контента:
<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>
Элемент <div class="email">
совпадает с элементами <content select="div">
и <content select=".email">
. Сколько раз появляется адрес электронной почты Боба и какими цветами?
Ответ заключается в том, что адрес электронной почты Боба появляется один раз и имеет желтый цвет.
Причина в том, что, как знают люди, взламывающие Shadow DOM, построение дерева того, что на самом деле отображается на экране, похоже на огромную вечеринку. Элемент контента — это приглашение, которое позволяет контенту из документа попасть в закулисную группу рендеринга Shadow DOM. Эти приглашения доставляются по порядку; кто получит приглашение, зависит от того, кому оно адресовано (то есть атрибут select
). Содержимое, однажды приглашенное, всегда принимает приглашение (кто бы не стал?!) и уходит. Если последующее приглашение будет отправлено на этот адрес еще раз, что ж, никого нет дома, и оно не придет на вашу вечеринку.
В приведенном выше примере <div class="email">
соответствует как селектору div
, так и селектору .email
, но поскольку элемент содержимого с селектором div
находится в документе раньше, <div class="email">
переходит к желтой стороне, и никто не может перейти к синей стороне. (Возможно, поэтому он такой синий, хотя несчастье любит компанию, так что никогда не знаешь.)
Если что-то не приглашено ни на одну из сторон, то оно вообще не будет визуализировано. Именно это произошло с текстом «Привет, мир» в самом первом примере. Это полезно, когда вы хотите достичь радикально другого рендеринга: напишите семантическую модель в документе, что доступно для сценариев на странице, но скрыть ее для целей рендеринга и подключить ее к действительно другой модели рендеринга в Shadow Dom с использованием JavaScript.
Например, у HTML есть хороший сборщик свиданий. Если вы пишете <input type="date">
вы получаете аккуратный календарь всплывающего окна. Но что, если вы хотите позволить пользователю выбрать ряд дат для своих десертных отпусков острова (вы знаете ... с гамаками, сделанными из красных лоз.) Вы установили свой документ таким образом:
<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>
Но создайте Shadow Dom, который использует таблицу для создания гладкого календаря, который подчеркивает диапазон дат и так далее. Когда пользователь нажимает дни в календаре, компонент обновляет состояние на входах StartDate и EndDate; Когда пользователь представляет форму, значения из этих входных элементов отправляются.
Почему я включил этикетки в документ, если они не будут отображаться? Причина в том, что если пользователь просматривает форму с браузером, который не поддерживает Shadow Dom, форма все еще используется, но не так красиво. Пользователь видит что -то вроде:
<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>
Вы передаете Shadow Dom 101
Это основы Shadow Dom - вы проходите Shadow Dom 101! Например, вы можете сделать больше с Shadow Dom, вы можете использовать несколько тени на одном теневом хосте или вложенных тени для инкапсуляции или архитектуры вашей страницы, используя модель, управляемые видами (MDV) и Shadow Dom. И веб -компоненты - это больше, чем просто тень Dom.
Мы объясняем их в последующих постах.
,Введение
Веб -компоненты представляют собой набор передовых стандартов, которые:
- Сделать возможным строительство виджетов
- ... который можно надежно использовать повторно
- ... и которые не сломают страницы, если следующая версия компонента изменит детали внутренней реализации.
Значит ли это, что вы должны решить, когда использовать HTML/JavaScript, а когда использовать веб -компоненты? Нет! HTML и JavaScript могут сделать интерактивные визуальные вещи. Виджеты являются интерактивными визуальными вещами. Имеет смысл использовать ваши навыки HTML и JavaScript при разработке виджета. Стандарты веб -компонентов предназначены для того, чтобы помочь вам сделать это.
Но существует фундаментальная проблема, которая заставляет виджеты, созданные из HTML и JavaScript, трудно использовать: дерево DOM внутри виджета не инкапсулируется с остальной части страницы. Отсутствие инкапсуляции означает, что таблица стилей вашего документа может случайно применить к деталям внутри виджета; Ваш JavaScript может случайно изменить детали внутри виджет; Ваши идентификаторы могут перекрываться с идентификаторами внутри виджета; и так далее.
Веб -компоненты состоит из трех частей:
Shadow Dom решает проблему инкапсуляции дерева DOM. Четыре части веб -компонентов предназначены для совместной работы, но вы также можете выбрать и выбрать, какие части веб -компонентов использовать. Этот урок показывает вам, как использовать Shadow Dom.
Привет, теневой мир
С Shadow Dom элементы могут получить новый вид узла, связанный с ними. Этот новый вид узла называется корнем тени . Элемент, который имеет корень тени, связан с ним, называется теневым хостом. Содержание теневого хоста не отображается; Вместо этого содержание корня тени.
Например, если у вас была наценка, как это:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
тогда вместо
<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>
Ваша страница выглядит как
<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>
Мало того, если JavaScript на странице спрашивает, что такое textContent
кнопки, он не получит «こんにちは、影の世界!», Но «Привет, мир!» Потому что поддеревание DOM под корнем тени инкапсулируется.
Отделение контента от презентации
Теперь мы рассмотрим использование Shadow Dom для отделения контента от презентации. Допустим, у нас есть этот имени тег:
<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>
Вот разметка. Это то, что вы напишите сегодня. Он не использует 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>
Поскольку в дереве DOM не хватает инкапсуляции, вся структура тега имени подвергается воздействию документа. Если другие элементы на странице случайно используют те же имена классов для стиля или сценариев, у нас будет плохое время.
Мы можем избежать плохого времени.
Шаг 1: Скрыть детали презентации
Семантически мы, вероятно, только заботимся о том, что:
- Это метка имени.
- Имя «Боб».
Во -первых, мы пишем разметку, которая ближе к истинной семантике, которую мы хотим:
<div id="nameTag">Bob</div>
Затем мы помещаем все стили и DOV, используемые для презентации в A <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>
На данный момент «Боб» - единственное, что отображается. Поскольку мы перенесли элементы DOM презентации внутри элемента A <template>
, они не отображаются, но их можно получить из JavaScript. Мы делаем это сейчас, чтобы заполнить корень тени:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Теперь, когда мы настроили корень Shadow, метка имени снова отображается. Если бы вам пришлось щелкнуть правой кнопкой мыши по тегу имени и проверить элемент, который вы видите, что это сладкая, семантическая разметка:
<div id="nameTag">Bob</div>
Это демонстрирует, что, используя Shadow Dom, мы спрятали детали презентации тега имени из документа. Детали презентации инкапсулируются в теневой DOM.
Шаг 2: Отдельный контент от презентации
Наш имени тег теперь скрывает данные презентации со страницы, но на самом деле он не отделяет презентацию от контента, потому что, хотя контент (имя «Боб») находится на странице, именем, которое отображается, является тем, которое мы скопировали в корне Shadow. Если мы хотим изменить имя на теге имени, нам нужно сделать это в двух местах, и они могут выйти из синхронизации.
Элементы HTML являются композиционными - например, вы можете положить кнопку в таблицу. Композиция - это то, что нам нужно здесь: Имя тега должна быть композицией красного фона, «Привет!» Текст, и контент, который находится на теге имени.
Вы, автор компонентов, определите, как композиция работает с вашим виджетом, используя новый элемент под названием <content>
. Это создает точку вставки в представлении виджета, а также содержание вишневой точки вставки от теневого хоста, которое будет представлено в этот момент.
Если мы изменим разметку в Shadow Dom на это:
<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>
Когда тег имени отображается, содержание теневого хоста проецируется в то место, которое появляется элемент <content>
.
Теперь структура документа проще, потому что имя только в одном месте - документ. Если вашей странице нужно обновить имя пользователя, вы просто пишете:
document.querySelector('#nameTag').textContent = 'Shellie';
И это все. Рендеринг тега имени автоматически обновляется браузером, потому что мы проецируем содержимое тега имени на место с <content>
.
<div id="ex2b">
Теперь мы достигли разделения содержания и презентации. Контент находится в документе; Презентация находится в теневой доме. Они автоматически синхронизируются в браузере, когда приходит время что -то сделать.
Шаг 3: Прибыль
Отделяя контент и презентацию, мы можем упростить код, который манипулирует контентом - в примере тега имени этот код должен иметь дело только с простой структурой, содержащей один <div>
вместо нескольких.
Теперь, если мы изменим нашу презентацию, нам не нужно менять какой -либо код!
Например, скажем, мы хотим локализовать наш имя. Это все еще тег имени, поэтому семантическое содержание в документе не меняется:
<div id="nameTag">Bob</div>
Код настройки корня Shadow остается прежним. То, что попадает в изменения корня тени:
<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>
Это большое улучшение по сравнению с ситуацией в Интернете, потому что ваш код обновления имени может зависеть от структуры компонента , которая является простым и последовательным. Ваш код обновления имени не должен знать структуру, используемую для рендеринга. Если мы рассмотрим то, что отображается, имя появляется вторым на английском языке (после «Привет! Меня зовут»), но сначала на японском (до «と申します»). Это различие является семантически бессмысленным с точки зрения обновления отображаемого имени, поэтому код обновления имени не должен знать об этой детали.
Дополнительный кредит: расширенная проекция
В приведенном выше примере элемент <content>
Элемент выбивает весь контент от хоста Shadow. Используя атрибут select
, вы можете управлять тем, что проект элемента контента. Вы также можете использовать несколько элементов контента.
Например, если у вас есть документ, который содержит это:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
и корень тени, который использует селекторы CSS для выбора конкретного контента:
<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>
Элемент <div class="email">
соответствует как элементы <content select="div">
, так и <content select=".email">
. Сколько раз появляется адрес электронной почты Боба и в каких цветах?
Ответ заключается в том, что адрес электронной почты Боба появляется один раз, и он желтый.
Причина в том, что, как знают люди, которые взламывают Shadow Dom, построив дерево того, что фактически отображается на экране, похоже на огромную вечеринку. Элемент контента - это приглашение, которое позволяет контенту из документа в сторону рендеринга Shadow Shadow Dom. Эти приглашения доставляются по порядку; Кто получает приглашение, зависит от того, кого оно адресовано (то есть, select
атрибут.) Содержание, когда -то приглашенное, всегда принимает приглашение (кто не будет?!), и вне его. Если последующее приглашение снова отправлено на этот адрес, ну, никто не дома, и оно не приходит на вашу вечеринку.
В приведенном выше примере <div class="email">
<div class="email">
как селектор div
, так div
.email
. (Возможно, поэтому он такой синий, хотя страдания любит компанию, так что вы никогда не знаете.)
Если что -то приглашается на вечеринки , то это вообще не визуализируется. Это то, что случилось с текстом «Привет, мир» в самом первом примере. Это полезно, когда вы хотите достичь радикально другого рендеринга: напишите семантическую модель в документе, что доступно для сценариев на странице, но скрыть ее для целей рендеринга и подключить ее к действительно другой модели рендеринга в Shadow Dom с использованием JavaScript.
Например, у HTML есть хороший сборщик свиданий. Если вы пишете <input type="date">
вы получаете аккуратный календарь всплывающего окна. Но что, если вы хотите позволить пользователю выбрать ряд дат для своих десертных отпусков острова (вы знаете ... с гамаками, сделанными из красных лоз.) Вы установили свой документ таким образом:
<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>
Но создайте Shadow Dom, который использует таблицу для создания гладкого календаря, который подчеркивает диапазон дат и так далее. Когда пользователь нажимает дни в календаре, компонент обновляет состояние на входах StartDate и EndDate; Когда пользователь представляет форму, значения из этих входных элементов отправляются.
Почему я включил этикетки в документ, если они не будут отображаться? Причина в том, что если пользователь просматривает форму с браузером, который не поддерживает Shadow Dom, форма все еще используется, но не так красиво. Пользователь видит что -то вроде:
<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>
Вы передаете Shadow Dom 101
Это основы Shadow Dom - вы проходите Shadow Dom 101! Например, вы можете сделать больше с Shadow Dom, вы можете использовать несколько тени на одном теневом хосте или вложенных тени для инкапсуляции или архитектуры вашей страницы, используя модель, управляемые видами (MDV) и Shadow Dom. И веб -компоненты - это больше, чем просто тень Dom.
Мы объясняем их в последующих постах.