Что такое Shadow DOM?

Spread the love

Ранее я написал статью “Что такое DOM?” Напомним, что объектная модель документа является представление HTML документа в виде дерева объектов. Оно используется браузерами для определения того, что отображать на странице, и кодом Javascript для изменения содержимого, структуры или стиля страницы.

Для примера взглянем на следующий HTML документ:

<!doctype html>
<html lang="en">
 <head>
   <title>My first web page</title>
  </head>
 <body>
    <h1>Hello, world!</h1>
    <p>How are you?</p>
  </body>
</html>

Этот документ может быть представлен в виде DOM дерева.

  • html
    • head
      • title
        • My first web page
    • body
      • h1
        • Hello, world!
      • p
        • How are you?

Вы, возможно, слышали о таких терминах, как «Shadow DOM» и «Virtual DOM». Хотя они, конечно, связаны с оригинальным DOM, но по сути своей это совершенно разные концепции. В этой статье я расскажу, что конкретно представляет собой теневой DOM и чем он отличается от исходного DOM. В следующей статье я расскажу что такое виртуальный DOM.

Все вокруг глобально 👍🏾! Стоп, все глобально 👎🏾

Все элементы и стили в HTML-документе и, следовательно, в DOM, находятся в одной большой глобальной области. Любой элемент на странице может быть доступен с помощью метода document.querySelector(), независимо от того, насколько глубоко он вложен в документ или где он находится. Точно так же CSS, примененный к документу, может выбрать любой элемент, независимо от того, где он находится.

Такое поведение может быть очень удобным, если мы хотим применить стили ко всему документу. Невероятно полезно иметь возможность выбирать каждый отдельный элемент на странице и устанавливать, например, их размеры одной строкой.

* { box-sizing: border-box }

С другой стороны, бывают случаи, когда элемент требует полной изоляции, и мы не хотим, чтобы на него влияли даже глобальные стили. Хорошим примером этого являются сторонние виджеты, такие как кнопка «Follow» для Twitter на некоторый страницах.


Такая кнопка может отображаться как обычная ссылка

Follow @ireaderinokun

Но если вы изучите этот элемент в DevTools, вы заметите, что кнопка является элементом <iframe>, который загружает небольшой документ со стилизованной кнопкой, которую вы на самом деле видите.

Это единственный способ, которым Twitter может гарантировать, что предполагаемый стиль их виджета останется незатронутым любым CSS в документе. Хотя существуют способы использования каскада стилей для достижения того же результата, но ни один другой метод не даст такой же гарантии, как  <iframe>, хотя и он не идеален.

Shadow DOM был создан для обеспечения возможности изоляции и компонентизации непосредственно на веб-платформе без необходимости полагаться на такие инструменты, как  <iframe>.

DOM внутри DOM

Вы можете думать о теневом DOM как о «DOM внутри DOM». Это собственное изолированное дерево DOM со своими элементами и стилями, полностью изолированное от исходного DOM.

Хотя только недавно его стали использовать программисты, теневой DOM годами использовался пользовательскими агентами для создания и оформления сложных компонентов, таких как элементы формы. Для примера возьмем элемент ввода диапазона. Чтобы создать его на странице, все, что нам нужно сделать, это добавить следующий элемент:

<input type="range">

Если мы посмотрим глубже, мы увидим, что этот один элемент  <input> фактически состоит из нескольких меньших элементов  <div> , управляющих дорожкой и ползунком.

Это делается с помощью теневого DOM. Элемент, который предоставляется HTML-документу как простой  <input>, но в реальности он состоит из нескольких элементов и стилей, связанных с компонентом, которые не являются частью глобальной области видимости DOM.

Как работает shadow DOM

Чтобы проиллюстрировать, как работает теневой DOM, давайте воссоздаем кнопку Twitter “follow” , используя теневой DOM вместо  <iframe>.

Сначала мы начнем с shadow host  (теневого хоста). Это обычный элемент HTML в исходном DOM, к которому мы хотим присоединить новый теневой DOM. Для такого компонента, как кнопка «Follow», он также может содержать запасной элемент, который мы хотели бы отобразить, если Javascript не был включен на странице или теневой DOM не поддерживается.

<span class="shadow-host">
  <a href="https://twitter.com/ireaderinokun">
     Follow @ireaderinokun
  </a>
</span>

Обратите внимание, что мы не просто использовали элемент <a>  в качестве теневого хоста, поскольку некоторые элементы, в первую очередь интерактивные элементы, не могут быть теневыми хостами.

Чтобы прикрепить теневой DOM к нашему хосту, мы используем метод  attachShadow().

const shadowEl = document.querySelector(".shadow-host");
const shadow = shadowEl.attachShadow({mode: 'open'});

Это создаст пустой  shadow root (теневой корень) как дочерний элемент нашего теневого хоста. Теневой корень – это начало нового теневого DOM, так же, как  <html> элемент является началом исходного DOM. Мы можем увидеть наш теневой корень в инспекторе devtools с помощью #shadow-root.

Хотя теперь дочерние элементы HTML видны в инспекторе, они больше не видны на странице, так как заработал теневой корень.

Далее мы хотим создать контент для формирования нашего нового теневого дерева. Чтобы создать нашу кнопку «Follow», нам нужен только новый элемент  <a>  , который будет почти таким же, как и у уже имеющейся резервной ссылки, но со значком.

const link = document.createElement("a");
link.href = shadowEl.querySelector("a").href;
link.innerHTML = `
    <span aria-label="Twitter icon"></span> 
    ${shadowEl.querySelector("a").textContent}
`;

Далее мы добавим этот новый элемент в нашу теневую DOM так же, как добавляем любой элемент в качестве дочернего элемента к другому с помощью метода appendChild().

shadow.appendChild(link);

На данный момент, наш элемент должен выглядет как то так:

Наконец, мы можем добавить несколько стилей, создав <style>  элемент и добавив его к теневому корню.

const styles = document.createElement("style");
styles.textContent = `
a, span {
  vertical-align: top;
  display: inline-block;
  box-sizing: border-box;
}

a {
    height: 20px;
    padding: 1px 8px 1px 6px;
    background-color: #1b95e0;
    color: #fff;
    border-radius: 3px;
    font-weight: 500;
    font-size: 11px;
    font-family:'Helvetica Neue', Arial, sans-serif;
    line-height: 18px;
    text-decoration: none;   
}

a:hover {  background-color: #0c7abf; }

span {
    position: relative;
    top: 2px;
    width: 14px;
    height: 14px;
    margin-right: 3px;
    background: transparent 0 0 no-repeat;
    background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}
`;

shadow.appendChild(styles);

В итоге наш элемент должен выглядеть как то так:

DOM против shadow DOM

В некотором смысле, теневой DOM является «облегченной» версией DOM. Как и DOM, он является представлением элементов HTML, которое используется для определения того, что следует отображать на странице, и позволяет изменять элементы. Но в отличие от DOM, теневой DOM не использует глобальные стили документа. Теневой DOM, как следует из названия, всегда присоединен к элементу в обычном DOM. Без базовых элементов DOM, теневой DOM не существует.

Оригинал


Spread the love

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *