Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. HTML & CSS
Code

Введение в Shadow DOM

by
Difficulty:BeginnerLength:LongLanguages:

Russian (Pусский) translation by AlexBioJS (you can also view the original English article)

Взгляните на любую современную веб-страницу и вы заметите, что в ней неизменно присутствует контент, собранный воедино с различных ресурсов; она может содержать виджеты для шаринга (публикации) контента в социальных сетях от Twitter или Facebook или виджет видеоплейера от Youtube, она может предоставлять персонализированную рекламу с некоторых рекламных серверов (* веб-сервер, хранящий рекламную информацию, используемую в онлайновом маркетинге, и доставляющий ее на различные цифровые платформы. Здесь и далее примеч. пер.) или содержать некоторые вспомогательные скрипты или стили со сторонних библиотек, которые хостятся на CDN (* content delivery network – сеть доставки контента) и т.д. И если в вебе все основано на HTML (чему отдают предпочтение сегодня), то существует высокая вероятность возникновения конфликтных ситуаций между разметкой, скриптами или стилями, поставляемыми с разных источников.Обычно для предотвращения этих ситуаций используются пространства имен (* набор правил именования, регулирующий видимость объектов в программе или хост-компьютеров в компьютерной сети), благодаря чему проблема частично решается, однако при этом не обеспечивается инкапсуляция (*  в ООП - сокрытие внутренней структуры данных и реализации методов объекта от остальной программы, т. е.  включение в объект всей необходимой информации таким образом, чтобы другим объектам не требовалось знание его внутренней структуры. Доступен только интерфейс объекта, через который осуществляется все взаимодействие с ним).

Инкапсуляция – один из китов, на которые опирается парадигма объектно-ориентированного программирования, и обычно используется для ограничения внутреннего представления объекта от внешнего мира.

Возвращаясь к нашей проблеме, скажу, что мы безусловно можем инкапсулировать код JavaScript при помощи замыканий или паттерна модуль, однако можем ли мы применить этот же подход и к разметке HTML? Представьте, что нам необходимо создать виджет UI (* пользовательский интерфейс); можем ли мы скрыть детали реализации нашего виджета от кода JavaScript и CSS, подключенного на странице, который пользуется нашим виджетом? Или наоборот, можем ли мы предотвратить нарушение работы нашего виджета или нарушения его внешнего вида из-за влияния кода, который пользуется нашим виджетом.


Решение проблемы при помощи Shadow DOM

Единственное решение, при котором создается граница между написанным вами кодом и кодом, который его использует, довольно безобразное и заключается в использовании громоздкого элемента iFrame, многие атрибуты которого считаются устаревшими (* является дочерним контекстом для браузера, за счет которого в текущую страницу встраивается другая страница HTML). Так должны ли мы всегда приспосабливаться к этому подходу?

Уже нет! Shadow DOM предоставляет нам элегантный способ перекрытия обычного поддерева DOM специальным фрагментом документа (* интерфейс DocumentFragment представляет собой минимальный объект документа, у которого нет родителя. Используется для хранения сегмента структуры документа, состоящей из узлов. Поскольку он не является частью древовидной структуры активного документа, то изменения, внесенные во фрагмент, не влияют на документ), который содержит другое дерево узлов, недоступных для скриптов и стилей. Интересно то, что это вовсе не новинка! Различные браузера уже использовали эту методологию для реализации нативных виджетов вроде date (* виджет для выбора времени), sliders (* слайдер), audio (* аудиоплеер), video player (* видеоплеер) и т.д.

Активируем показ Shadow DOM

На момент написания этого руководства текущая версия Chrome (v29) поддерживает инспектирование Shadow DOM при помощи Chrome DevTools. Откройте Devtools и нажмите кнопку с иконкой зубчиков шестеренки справа внизу экрана для открытия панели Settings, прокрутите бегунок немного вниз и увидите флажок для активации показа Shadow DOM.

Turn on Shadow DOM

Теперь, когда мы активировали его в нашем браузере, давайте ознакомимся с внутренним устройством аудиоплеера по умолчанию. Просто введите:

в вашу HTML-разметку. Благодаря нему показывается следующий нативный аудиоплеер в браузерах, что его поддерживают:

audio_player

Теперь давайте, проинспектируйте этот виджет аудиоплеера, который только что создали. Вау!

Shadow DOM of Native Date Widget

В инспекторе показывается внутренняя организация аудиоплеера, которую без активации показа Shadow DOM было скрыто. Как мы видим в элементе audio используется  фрагмент документа для размещения внутреннего контента виджета и его добавления в элемент-контейнер (известный как Shadow Host).

Shadow Host и Shadow Root

  • Shadow Host: элемент DOM, в котором размещается поддерево Shadow DOM, или узел DOM, в котором располагается Shadow Root.
  • Shadow Root: корень поддерева DOM, в котором располагаются узлы DOM. Это специальный узел, при помощи которого создается граница между обычными узлами DOM и узлами Shadow DOM. Именно эта граница позволяет инкапсулировать узлы Shadow DOM от любого кода JavaScript или CSS на странице, которая его использует.
  • Shadow DOM: позволяет объединять множество поддеревьев DOM в одно большое дерево. На следующем изображении из рабочего проекта W3C отлично проиллюстрирована концепция перекрытия узлов. Тут показано, как они выглядят до добавления контента Shadow Root в элемент Shadow Host:
    Normal Document Tree Shadow DOM Subtrees

    После выполнения кода для добавления Shadow DOM дерево Shadow заменяет контент Shadow Host.

    Composition Complete

    Этот процесс перекрытия узлов часто называют композицией (*  в ООП – метод создания нового объекта путём объединения старых и новых частей, в противоположность наследованию).

  • Shadow Boundary (* граница): отмечена пунктирной линией на изображении выше. При помощи нее отмечается граница между обычным миром DOM и миром Shadow DOM. Скрипты с обеих сторон не могут пересечь эту границу и нарушить работу кода на другой стороне.

Hello Shadow DOM World (* первая простейшая программа, которую создают новички в области компьютерных наук)

Хватит болтовни, давайте приступим к написанию некоторого кода. Предположим, что у нас имеется следующая разметка, при помощи которой показывается простое сообщение с приветствием.

Добавьте следующий код JavaScript или воспользуйтесь Fiddle (* фрагмент кода HTML, CSS или JavaScript, размещаемый в онлайн сообществе JSFiddle):

Здесь мы создаем Shadow Root при помощи функции webkitCreateShadowRoot(), добавляем его к Shadow Host и затем просто изменяем контент.

Обратите внимание на префикс, специфичный для определенного производителя браузеров, – webkit перед именем функции. Он указывает на то, что эта функциональная возможность (* Shadow DOM) сейчас поддерживается только в некоторых браузерах, работающих на основе webkit (* движок браузера).

Если бы вы выполнили код этого примера в браузере с поддержкой Shadow DOM, то увидели бы "Hello Shadow DOM World" вместо "Welcome to My World", поскольку узлы Shadow DOM перекрыли бы обычные.

Оговорка: Как некоторые из вас могли заметить, мы смешиваем разметку с кодом скрипта, что обычно не рекомендуется, и Shadow DOM не является исключением. Мы преднамеренно не использовали шаблоны сейчас (* позволяет вам объявлять фрагменты DOM, которые подвергаются парсингу, неактивны при загрузке страницы и могут быть активированы позже при выполнении кода), чтобы избежать путаницы. Shadow DOM также предоставляет нам решение этой проблемы, и мы рассмотрим его очень скоро.


Некоторые сведения относительно Shadow Boundary

Если вы попробуете обратиться к контенту отображенного дерева при помощи JavaScript следующим образом:

то получите изначальный контент "Welcome to My World", а не контент, который собственно отображается на странице, поскольку дерево Shadow DOM инкапсулировано от любых скриптов.  Это также означает, что виджет, который вы создаете при помощи Shadow DOM, защищен от влияния любых нежелательных/несовместимых скриптов, уже присутствующих на странице.

Инкапсуляция стилей

Подобным образом, пересечение Shadow Boundary любым селектором CSS запрещено. Ознакомьтесь со следующим кодом, в котором мы применили к элементам списка красный цвет, однако этот стиль применяется только к узлам, которые являются частью родительской страницы, и на элементы списка, которые являются частью Shadow Root, он не влияет.

Вы можете увидеть код в действии на Fiddle. Подобная инкапсуляция срабатывает даже если мы меняем направление обхода дерева (* при применении стилей). Любые стилевые правила, определенные внутри Shadow DOM, не влияют на родительский документ и остаются только в области видимости Shadow Root. Ознакомьтесь с этим Fiddle в качестве примера, где мы применяем синий цвет к элементам списка в Shadow DOM, однако на элементы списка родительского документа этот стиль не влияет.

Но при этом имеется одно примечательное исключение; Shadow DOM предоставляет нам возможность задать стилевое оформление для Shadow Host, узла DOM, в котором располагается Shadow DOM. В идеале этот узел располагается за пределами Shadow Boundary и не является частью Shadow Root, однако за счет использования правила @host мы можем добавить стилевое оформление к Shadow Host так, как мы сделали для сообщения с приветствием в примере ниже.

Ознакомьтесь с этим Fiddle, где мы добавили стилевое оформление для сообщения с приветствием в Shadow Host при помощи стилевых правил, заданных в Shadow DOM.

Создание зацепок (* специальная точка входа; место в программе, куда можно подсоединить дополнительный код (обычно для расширения ее функциональных возможностей)) для добавления стилевого оформления

Как разработчик виджета я мог бы захотеть, чтобы у пользователя моего виджета была возможность добавить собственное стилевое оформление для определенных элементов. Это реализуемо за счет добавления дырки в Shadow Boundary при помощи пользовательских псевдо-элементов (* элемент DOM внутри Shadow Root пользовательского элемента, для которого автор явно добавил возможность задания правил стилевого оформления из-за пределов Shadow Root при помощи псевдо-селекторов). Это подобно тому, как некоторые браузера предоставляют разработчикам зацепки для добавления стилевого оформления к некоторым внутренним элементам нативного виджета. Например для того чтобы добавить стилевое оформление для ползунка и линейки нативного слайдера, вы можете использовать ::-webkit-slider-thumb и ::webkit-slider-runnable-track следующим образом:

Форкните (* создайте собственную копию) этого Fiddle и добавьте собственное стилевое оформление к нему.

Переориентация события

Если событие, которое возникает на одном из узлов Shadow DOM, пересекает Shadow Boundary, то оно переориентируется таким образом, чтобы относиться к Shadow Host для сохранения инкапсуляции. Рассмотрим следующий код:

За счет него отображаются два поля для ввода текста: одно за счет обычного DOM и другое благодаря Shadow DOM, и затем устанавливается обработчик события click для объекта document. Теперь, при нажатии по второму полю для ввода текста, событие возникает внутри Shadow DOM, и при его пересечении через Shadow Boundary оно меняется так, чтобы целевой элемент поменялся на элемент <div> Shadow Host вместо поля для ввода текста <input>. Также у нас тут представлен новый элемент <template>; по своей концепции он подобен решениям для создания шаблонов для клиентской стороны типа Handlebars и Underscore, однако не так продвинут и недостаточно поддерживается некоторыми браузерами. Тем не менее, использование шаблонов для написания Shadow DOM – идеальный способ по сравнению с применением тегов в скрипте, как мы делали до сих пор в этом руководстве.


Разделяем то, о чем нужно будет позаботиться

Мы уже знаем, что всегда следует разделять собственно контент и его представления; в Shadow DOM не должен содержаться никакой контент, который необходимо в итоге показать пользователю. Вместо этого контент должен всегда располагаться на исходной странице, а не скрываться в шаблоне Shadow DOM. При выполнении композиции этот контент затем должен быть спроецирован в соответствующую точку вставки (* место в документе, где будут выполняться некоторые действия), указанную в шаблоне Shadow DOM. Давайте перепишем наш пример «Hello World», помня о вышеупомянутом разделении; с рабочим примером можете ознакомиться на Fiddle.

После отображения страницы контент Shadow Host проецируется в место, где появляется элемент <content> (* ранее использовался в Shadow DOM в качестве точки вставки. Не предполагалось, что он будет использоваться в обычном HTML. Заменен элементом , что используется для создания точки в DOM, куда может быть вставлен Shadow DOM). Это очень упрощенный пример, в котором в <content> при композиции помещается все, что находится в Shadow Host. Однако в него также без проблем может помещаться и выборочный контент из Shadow Host при помощи атрибута select, как показано ниже:

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


Web Components

Как вы уже, скорее всего, знаете, Shadow DOM – часть спецификации Web Components, которая предлагает и другие изящные инструменты вроде:

  1. Templates – используются для размещения неактивной разметки, которую нужно будет использовать позже. Под неактивностью подразумевается, что все изображения в разметке не загружаются и подключенные скрипты не выполняются до тех пор, пока контент шаблона не становится собственно частью страницы.
  2. Decorators – используются для применения шаблонов на основе селекторов CSS, и, таким образом, можно считать, что они украшают существующие элементы за счет улучшения их представления.
  3. HTML Imports – предоставляет нам возможность повторного использования других документов HTML в нашем документе без необходимости явного выполнения запросов XHR (* XMLHttpRequest) и написания обработчиков событий для него.
  4. Custom Elements (* пользовательские элементы) – позволяют нам определять новые типы элементов HTML, которые затем можно объявлять в разметке. Например, если вы хотите создать ваш собственный виджет для навигации, вы определяете ваш элемент для навигации, наследуя свойства и методы от HTMLElement и предоставляя определенные функции обратного вызова, необходимые для реализации его жизненного цикла, при помощи которых задается поведение при возникновении определенных событий (construction, change, destruction) для виджета, и просто используете этот виджет в вашей разметке в виде <myAwesomeNavigation attr1="value1"..> </myAwesomeNavigation>. Таким образом, пользовательские элементы по существу предоставляют нам способ объединить все возможности Shadow DOM, скрывая внутренние детали и объединяя все воедино.

Я не буду долго болтать о других аспектах спецификации Web Components в этом руководстве, однако хорошо, если мы будем помнить, что вместе они позволяют нам создавать виджеты UI, которые можно повторно использовать и которые выглядят одинаково в различных браузерах, а также полностью инкапсулированы от всех скриптов и стилей страницы, которая использует их.


Заключение

Спецификация Web Components – развивающийся проект, и приведенный код  примеров, что работает сегодня, может не работать в следующих версиях. Например ранее в руководствах на эту тему использовался метод webkitShadowRoot(), который более не срабатывает; вместо него используется createWebkitShadowRoot() для создания Shadow Root. Так что, если вы хотите воспользоваться этим руководством для создания некоторых крутых демоверсий при помощи Shadow DOM, то всегда лучше обратиться к спецификации за подробностями.

В настоящий момент только Chrome и Opera поддерживают эту технологию, так что я был бы настороже при добавлении какого-либо Shadow DOM в мои конечные работы, однако учитывая то, что Google предоставляет Polymer, работающий на основе Web Components, и Polyfills (* polyfill – тип прокладки,  которая добавляет в старые браузеры поддержку возможностей, которые в современных браузерах являются встроенными), предназначенный для нативной поддержки Shadow DOM браузерами, эту технологию должен осваивать каждый веб-разработчик.

Вы также можете следить за событиями, происходящими в мире Shadow DOM, подписавшись на канал Google+ Channel. Также ознакомьтесь с инструментом Shadow DOM Visualizer (* визуализатор работы Shadow DOM), который помогает вам визуализировать то, как Shadow DOM отображается в браузере.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.