что такое теневой дом
<video controls autoplay name="media">
<source id="mp4" src="trailer.mp4" type="video/mp4">
</video>
Выше приведен самый простой тег видео, который имеет громкость по умолчанию и другие кнопки. В исходном коде вообще нет следов. Так откуда берутся эти узлы?
Это теневой DOM. Элементы управления видеоэкраном в браузере выглядят так:
Обнаружить#shadow-root
Он серый, это для того, чтобы браузер показывал, что в теневом DOM содержимое, представляющее другие части страницы, не будет влиять на внутреннюю часть (можно проникнуть специфическим способом, о котором будет рассказано далее)
Контент конкретно относится к селекторам css и коду javascript.
Короче говоря, Shadow DOM — это новая спецификация HTML, которая позволяет разработчикам инкапсулировать компоненты HTML (аналогично компонентам vue, извлекая отдельные части html, css и js).
Зачем использовать теневой DOM
Вы должны быть знакомы с названием Bootstrap Код обычно выглядит следующим образом:
<ul class="media-list">
<li class="media">
<div class="media-left">
<a href="#">
![](...)
</a>
</div>
<div class="media-body">
<h4 class="media-heading">Media heading</h4>
</div>
</li>
</ul>
очень проста и удобна в использовании, но у вас нет глубокого понимания этих вещей.Часто после того, как структура усложняется, остается куча шаблонов.Модификация - сложная проблема, и она затрагивает весь организм .
Преимущество теневого DOM в этом случае огромно, можно писать такие шаблоны
// 我是一个简洁的模板
<bootstrap-media-list>
<a href="#">
![](...)
</a>
<h4 class="media-heading">Media heading</h4>
</bootstrap-media-list>
Конечно, если вы хотите так писать, вам понадобятся js и css.
как использовать
Сначала запустите пример
<div class="widget">Hello, world!</div>
<script>
var host = document.querySelector('.widget');
var root = host.createShadowRoot();
root.textContent = '我在你的 div 里!';
</script>
Сначала указываемхост-узел(shadow host), а затем создайте теневой корень (shadow root) добавляет к нему текстовый узел, и в результате контент в хосте не отображается.
Как визуализировать содержимое в хост-узле
Только содержимое корня тени в основном недостойно, но содержимое узла хоста может сделать страницу более гибкой. мы должныcontent
Этикетка
<div class="pokemon">胖丁</div>
<template class="pokemon-template">
<h1>一只野生的<content></content>出现了!</h1>
</template>
<script>
var host = document.querySelector('.pokemon');
var root = host.createShadowRoot();
var template = document.querySelector('.pokemon-template');
root.appendChild(document.importNode(template.content, true));
</script>
<content>
Этикетка создаетточка вставкиБуду.pokemon
Текст внутри проецируется, что может быть удобно, когда несколько элементов совпадают.select
назначение атрибута
<div class="host">
<p>大慈大悲,由诸葛亮进化而来。</p>
<span class="name">观音姐姐兽</span>
</div>
<template class="root-template">
<dl>
<dt>名字</dt>
<dd><content select=".name"></content></dd>
</dl>
<p><content select=""></content></p>
</template>
<script>
var host = document.querySelector('.host');
var root = host.createShadowRoot();
var template = document.querySelector('.root-template');
root.appendChild(template.content);
</script>
можно использовать
select
Атрибуты, такие как селекторы, отображают проекции соответствующих элементов в хост-узле. Эта форма может не только изменить порядок потока DOM, но и сделать макет гибким.в конце шаблона
<content select=""></content>
Это своего рода жадное сопоставление, которое проецирует весь несопоставленный контент в хост-узле. Следует отметить, что размещение жадного сопоставления вверху приведет к проецированию всех узлов, и последующий выбор не получит спроецированный им контент.Все следующие эквиваленты:
<content></content>
<conent select=""></conent>
<content select="*"></content>
Рендеринг и инкапсуляция стилей
Давайте рассмотрим простой пример
<style>
button {
font-size: 18px;
font-family: '华文行楷';
}
</style>
<button>普通按钮</button>
<div></div>
<script>
var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML =
'<style>button { font-size: 24px; color: blue; } </style>'+
'<button>影子按钮</button>';
</script>
Существование границ в теневых узлах делает
shadow DOM
Стили и стили в обычном потоке DOM не мешают друг другу. Это作用域化的体现
, больше не беспокойтесь о конфликтующих стилях.
(: хост) селектор
:host
Является селектором псевдокласса для выбора хост-узла, мы можем расширить приведенный выше пример.
<style>
p {
font-size: 12px;
}
</style>
<p>我的文本</p>
<button>我的按钮</button>
<template class="shadow-template">
<style>
:host(p) {
color: green;
}
:host(button) {
color: red;
}
:host(*) {
font-size: 24px;
}
</style>
<content select=""></content>
</template>
<script>
var root1 = document.querySelector('p').createShadowRoot();
var root2 = document.querySelector('button').createShadowRoot();
var template = document.querySelector('.shadow-template');
root1.appendChild(document.importNode(template.content, true));
root2.appendChild(document.importNode(template.content, true));
</script>
В этом примере есть несколько моментов:
- Размер шрифта тега p составляет 12 пикселей = теневой стиль имеет меньший приоритет, чем стиль страницы
-
:host
Сборщик может использовать любой допустимый селектор,*
применить ко всему - Подключив разные хосты для рендеринга разного контента, можно добиться тематики.
Тематика выше не полная, она выбирается только в соответствии с элементом монтирования, то есть.parent > .child
, но мы также можем пройти:host-context
выполнить.parent < .child
следующим образом<div class="serious"> <p class="serious-widget"> serious-widget </p> </div> <div class="playful"> <p class="playful-widget"> playful-widget </p> </div> <template class="widget-template"> <style> :host-context(.serious) { width: 250px; height: 50px; background: tomato; } :host-context(.playful) { width: 250px; height: 50px; background: deepskyblue; } </style> <content></content> </template> <script> var root1 = document.querySelector('.serious-widget').createShadowRoot(); var root2 = document.querySelector('.playful-widget').createShadowRoot(); var template = document.querySelector('.widget-template'); root1.appendChild(document.importNode(template.content, true)); root2.appendChild(document.importNode(template.content, true)); </script>
Вышеупомянутый эффект очень хорош, вы можете создавать динамические компоненты
ps: псевдокласс, селектор псевдоэлементов также можно использовать напрямую, эффект согласуется с обычным узлом
(::контент) селектор
При использовании теневого DOM следует обеспечить разделение контента и представления, то есть текст должен исходить со страницы, а не быть погребенным в шаблоне теневого DOM. Итак, нам нужно визуализировать распределенные узлы в шаблоне.
<div class="widget">
<button>分布节点碉堡啦!</button>
</div>
<template class="widget-template">
<style>
::content > button {
color: white;
background: tomato;
border-radius: 10px;
border: none;
padding: 10px;
}
</style>
<content select=""></content>
</template>
<script>
var root = document.querySelector('.widget').createShadowRoot();
var template = document.querySelector('.widget-template');
root.appendChild(document.importNode(template.content, true));
</script>
сломать область видимости (::shadow)
Мы можем использовать в узле монтирования::shadow
,Например
<style>
.sign-up::shadow #username{
font-size: 20px;
border: 1px solid red;
}
</style>
<div class="sign-up"></div>
<template class="sign-up-template">
<style>
#username{
font-size: 12px;
}
</style>
<div>
<input type="text" id="username" placeholder="用户名">
</div>
</template>
<script>
var root = document.querySelector('.sign-up').createShadowRoot();
var template = document.querySelector('.sign-up-template');
root.appendChild(document.importNode(template.content, true));
</script>
Но недостаток в том, что он может проникать только один слой, но у нас все еще есть артефакт!
Многослойное проникновение (/глубокое/)
<style>
#foo /deep/ button {
color: red;
}
</style>
<div id="foo"></div>
<template>
<div id="bar"></div>
</template>
<script>
var root1 = document.querySelector('#foo').createShadowRoot();
var template = document.querySelector('template');
root1.appendChild(document.importNode(template.content, true));
var root2 = root1.querySelector('#bar').createShadowRoot();
root2.innerHTML = '<button>点我点我</button>';
</script>
разница в javascript
- Данные не выровнены по блокам и по-прежнему монтируются в
window
начальство
- Перенаправление событий (события, изначально привязанные к теневым узлам DOM, перенаправляются таким образом, что они кажутся связанными с хост-узлом)
<input id="normal-text" type="text" value="I'm normal text"> <div id="host"></div> <template> <input id="shadow-text" type="text" value="I'm shadow text"> </template> <script> var root = document.querySelector('#host').createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('click', function(e) { console.log(e.target.id + ' clicked!'); }); </script>
Вы можете видеть, что события в теневом узле проксируются хост-узлом.
блокировка событий
будет заблокирован в корне теневого узла при прослушивании следующих событий:
aborterror
select
change
load
reset
reset
resize
scroll
-
selectstar
<input id="normal-text" type="text" value="I'm normal text"> <div id="host"> <input id="distributed-text" type="text" value="I'm distributed text"> </div> <template> <div><content></content></div> <div> <input id="shadow-text" type="text" value="I'm shadow text"> </div> </template> <script> var root = document.querySelector('#host').createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('select', function(e) { console.log(e.target.id + ' text selected!'); }); </script>
Событие заблокировано в корне теневого узла и не можетducoment
, так что это не может быть проверено.
распределительный узел
Распределенные узлы относятся к ранее переданным<content>
Метка проецирует содержимое хост-узла, и распределительный узел не будет блокировать вышеперечисленное, потому что это всего лишь проекция фактического содержимого или смонтированное на хост-узле.