Теневой узел ShadowDOM

внешний интерфейс JavaScript Vue.js CSS

что такое теневой дом

<video controls autoplay name="media">
  <source id="mp4" src="trailer.mp4" type="video/mp4">
</video>

  Выше приведен самый простой тег видео, который имеет громкость по умолчанию и другие кнопки. В исходном коде вообще нет следов. Так откуда берутся эти узлы?
  Это теневой DOM. Элементы управления видеоэкраном в браузере выглядят так:

shadow DOM浏览器中体现
Отражено в теневых браузерах 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

  1. Данные не выровнены по блокам и по-прежнему монтируются в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>Метка проецирует содержимое хост-узла, и распределительный узел не будет блокировать вышеперечисленное, потому что это всего лишь проекция фактического содержимого или смонтированное на хост-узле.