Инкапсуляция в коде веб-приложений что такое shadow DOM


Оглавление (нажмите, чтобы открыть):

Является ли Shadow DOM быстрым, как Virtual DOM в React.js?

Осуществляет ли реализация Shadow DOM в моих проектах быстрее, чем виртуальная DOM, используемая React?

Виртуальный DOM

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

Тень DOM

Shadow dom в основном касается инкапсуляции реализации. Один пользовательский элемент может реализовать более или менее сложную логику в сочетании с более или менее сложным DOM. Все веб-приложение произвольной сложности может быть добавлено на страницу с помощью импорта и , но также более простые многоразовые и составные компоненты могут быть реализованы как настраиваемые элементы, где внутреннее представление скрыто в теневой DOM, например .

Инкапсуляция стиля Shadow DOM также предупреждает о том, что стили применяются случайно к элементам, которые дизайнер не намеревался, например, потому что используемая вами библиотека CSS или компонентов изменила селектор, который теперь применяется к другим элементам, которые используют одни и те же имена классов CSS. Стили, добавленные к компонентам, привязаны к этому компоненту, и предотвращается удаление или стилизация стилей.

Теневая DOM и производительность

Несмотря на то, что теневое DOM не касается производительности, во-первых, это также имеет последствия для производительности. Поскольку стили ограничены, браузер может сделать предположения о некоторых изменениях влиять только на ограниченную область страницы (теневое DOM настраиваемого элемента), которое может ограничить повторный рендеринг в области такого компонента, вместо повторного рендеринга всю страницу.

По этой причине комбинаторы >>> , /deep/ и ::shadow CSS, которые разрешали применять стили по границам DOM-шрифта, были устаревшими и вскоре могут быть удалены из Chrome (в других браузерах их никогда не было НАСКОЛЬКО МНЕ ИЗВЕСТНО). Простое существование этих комбинаторов предотвращает оптимизацию, упомянутую в предыдущем абзаце.

Angular2 использует преимущества обоих миров.

Он использует однонаправленный поток данных и запускает обнаружение изменений только на модели. Если он обнаруживает изменения, это приводит к обновлению DOM путем обновления привязок и создания структурных директив, таких как *ngFor , *ngIf . обновление DOM. Поэтому DOM обновляется только тогда, когда модель действительно изменилась.

Инкапсуляция веб-компонентов и привязка событий к теневым элементам DOM

Я начал подробно изучать веб-компоненты, прежде чем переходить на использование Polymer.

Я работал над простым примером для создания кнопки вращения, используя два элемента и один .

И элемент хоста:

Во время экспериментов я узнал, что JS/код не работает как do в shadow DOM, являющийся частью В приведенном выше примере я добавляю точки вставки ( ), а затем присоединяю прослушиватель событий к распределенным элементам.

  • Есть ли способ инкапсулировать реализацию прослушивателя событий?
  • Есть ли способ переместить элементы управления s и элементы в shadow dom, а затем добавить прослушиватель событий любым способом?

Является ли shadow DOM быстрым, как виртуальный DOM в React.Джей?

реализация Shadow DOM в моих проектах сделает их быстрее, как виртуальный DOM, который используется React?

3 ответа:

виртуальный дом

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

тень Дом

Shadow dom-это в основном инкапсуляция реализации. Один пользовательский элемент может реализовать более или менее сложную логику в сочетании с более или менее сложной DOM. Все веб-приложение произвольной сложности может быть добавлено на страницу с помощью импорта и но также более простые многоразовые и составные компоненты могут быть реализованы как пользовательские элементы, где внутреннее представление скрыто в теневом DOM, например .

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

тень DOM и производительность

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

вот почему >>> , /deep/ и ::shadow комбинаторы CSS, которые позволяли применять стили через границы теневого DOM, были устаревшие и подлежат удалению в ближайшее время из Chrome (другие браузеры никогда не имели их AFAIK). Само существование этих комбинаторов предотвращает вид оптимизации, упомянутый в предыдущем абзаце.

Angular2 использует преимущества обоих миров.

он использует однонаправленный поток данных и запускает обнаружение изменений только на модели. Если он обнаруживает изменения, он вызывает обновление DOM путем обновления Привязок и создания структурных директив, таких как *ngFor , *ngIf , . обновить дом. Поэтому DOM обновляется только тогда, когда модель фактически изменилась.

Angular2 использует shadow DOM (только с ViewEncapsulation.Native который в настоящее время не является стандартным), чтобы использовать возможности инкапсуляции стиля, предоставляемые браузером, или (текущее значение по умолчанию) просто эмулировать инкапсуляцию стиля путем перезаписи стилей, добавленных к компонентам, в качестве обходного пути, пока собственные теневые переменные DOM и CSS (для динамических глобальных изменений стиля) не станут широко доступными.

нет, Shadow DOM и Virtual DOM не связаны, хотя и имеют несколько схожее название:

виртуальный дом: React концепция сохранения двух копий DOM (оригинал и обновленный) по разным причинам. Перед рендерингом React различает два объекта, чтобы определить, следует ли применять обновление(ы) к фактическому дереву DOM. Это приводит к повышению производительности, так как мы обновляем только те части представления, которые этого требуют, а не весь экран.

теневой DOM: часть веб-компоненты как предложено W3C, что в основном позволяет инкапсулировать меньшие элементы DOM и стили CSS в один элемент DOM:

пример теневого элемента DOM

, фактически инкапсулирует следующие элементы:

таким образом, используя Shadow DOM, мы можем скрыть детали реализации нашего веб-элемента и передавайте только необходимую информацию подэлементам (т. е. height , width ), который, возможно, смутно, сильно напоминает идиому ReactJS прохождения props к компонентам.

информация, предоставляемая через:

Frontender Magazine

Введение


Веб-компоненты — это набор новых стандартов, которые:

  1. Позволяют создавать виджеты,
  2. … которые могут быть использованы повторно без риска сбоев
  3. … и которые не приводят к разрушению страницы, если в следующей версии соответствующего веб-компонента внесены изменения в детали реализации.

Значит ли это, что придётся определиться, где стоит использовать HTML/JavaScript, а где — веб-компоненты? Нет! С помощью HTML и JavaScript можно создавать разные визуальные интерактивные штуки. В том числе и виджеты. При разработке виджета целесообразно воспользоваться знаниями HTML и JavaScript. Веб-компоненты придуманы, чтобы вам в этом помочь.

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

Тем не менее, есть существенная проблема, из-за которой разрабатывать виджеты на HTML и JavaScript довольно сложно: дерево элементов виджета не инкапсулировано от остальной страницы. Отсутствие инкапсуляции значит, что таблица стилей вашего сайта может быть по ошибке применена к компонентам внутри виджета; ваш JavaScript может случайно изменить составляющие виджета; названия ваших идентификаторов могут оказаться идентичными названиям идентификаторов внутри виджета и так далее.

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

Веб-компоненты состоят из четырёх частей:

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

Спецификация теневой модели документа пока поддерживается только в Chrome 25, поэтому для этого API используется вендорный префикс webkit.

Здравствуй, мир теней

В спецификации теневой модели документа вводится новый тип элементов. Он называется корневым элементом теневого дерева (shadow root). Элемент, к которому привязан корневой элемент теневого дерева, называется ведущим элементом теневого дерева (shadow host). Содержимое ведущего элемента не отображается, вместо него отображается содержимое корневого элемента теневого дерева.

Например, если у вас такая разметка:

на вашей странице будет отображено

Это ещё не всё, если JavaScript страницы запросит textContent кнопки, ответом будет не «こんにちは、影の世界!», а «Здравствуй, мир!», потому что дочернее дерево инкапсулировано.

Пожалуй, здесь нарушено одно золотое правило: в теневое дерево нельзя помещать контент. Он должен быть помещён в основное дерево страницы, чтобы быть доступным скринридерам, поисковым системам и т.д. Теневое дерево предназначено для всей той семантически бессмысленной разметки, которая нужна, чтобы создать красивый и удобный виджет. А вот контент должен оставаться в коде страницы.

Конечно же, за нарушение этого правила вас никто не осудит; в сети вы свободны делать более-менее всё, что вам угодно. Однако, не перегибайте палку.

Разделение контента и представления

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

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

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

Этого можно избежать.

Шаг 1. Прячем описание представления

С точки зрения семантики нам нужны только следующие сведения:

  • Перед нами именная табличка.
  • На табличке имя «Сергей».

Сначала напишем разметку с учётом наших семантических требований:

Затем поместим все теги div и описание стилей, отвечающих за представление, в элемент :

На данном этапе отображается только «Сергей». Так как мы поместили элементы, отвечающие за представление, в элемент , они не отображаются, однако их можно извлечь с помощью JavaScript. Что мы сейчас и сделаем, чтобы заполнить корневой элемент теневого дерева:

Стандарты для создания шаблонов, вроде теневой модели документа, находятся на стадии становления. Элемент поддерживается в Chrome Canary. Корневой элемент теневого дерева можно заполнить также с помощью таких хорошо известных свойств и методов как innerHTML , appendChild , getElementById и т.д. Эта статья посвящена теневой модели документа, так что мы не будем вникать в работу элемента глубже. Если вы хотите узнать о нём больше, прочитайте статью «Новый HTML-тег template ».

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

Как видите, мы спрятали описание представления именной таблички, используя теневое дерево.

Шаг 2. Разделение контента и представления

В коде страницы теперь не видно описания представления именной таблички, однако нельзя говорить о разделении представления и контента. Ведь хоть на странице и присутствует контент (имя «Сергей»), однако на самом деле отображается имя, скопированное в корневой элемент теневого дерева. Чтобы изменить имя на табличке, его нужно поменять в двух местах, что в результате может привести к непоследовательности.

Элементы HTML композиционны — например, можно поместить кнопку в таблицу. Композиционность именно то, что нам в этом случае нужно: именная табличка должна быть композицией из красного фона, текста приветствия и имени на табличке.

Вы, создатель компонента, определяете, какая композиция должна быть у вашего виджета, используя новый элемент . Он создаёт точку вставки в представлении виджета, в которой выборочно отображается контент из ведущего элемента теневого дерева.

Изменим разметку теневого дерева на следующую:

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

Теперь структура страницы упрощена, ведь имя указано только в одном месте — в коде страницы. Если вам когда-нибудь потребуется обновить имя пользователя, нужно только прописать:

и всё. Отображение именной таблички автоматически обновляется браузером, потому что происходит проецирование её содержимого туда, где расположен .

Пример использования Shadow DOM:

Новое имя: Обновить


Теперь мы добились разделения контента и представления. Контент помещён в дерево страницы; представление — в теневое дерево. Когда приходит время что-либо отобразить, браузер их синхронизирует автоматически.

Шаг 3. Польза

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

Если нам нужно изменить представление, код в дереве страницы менять не нужно!

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

Исходный код в корневом элементе теневого дерева остаётся прежним. Изменяется только то, что в него подставляется:

Теперь мы получили именную табличку с текстом на японском языке:

Новое имя: Обновить

Автор фонового изображения — Майк Дауман (Mike Dowman); изображение использовано в согласии с условиями лицензии Creative Commons.

Это значительный шаг вперёд, ведь код для обновления имени зависит от структуры соответствующего компонента, которая теперь проста и последовательна. Ваш код для обновления имени не должен учитывать элементы, которые используются для отображения контента. Если взглянуть на то, что отображается, в русском языке имя идёт на втором месте (после «Здравствуйте! Меня зовут»), но первым в японском (перед «と申します»). Это различие не имеет никакого семантического значения на этапе обновления отображаемого имени, так что не должно быть необходимости учитывать этот нюанс в коде обновления имени.

Бонус: продвинутое проецирование

В примере выше элемент выбирает контент из ведущего элемента теневого дерева. Используя атрибут select можно контролировать что проецирует элемент . Также можно использовать несколько элементов .

Например, у нас есть такой код страницы:

и корневой элемент теневого дерева, который с помощью селекторов CSS проводит выборку определённого контента:

Примечание: с помощью select можно выбрать только те элементы, которые являются непосредственно дочерними по отношению к главному узлу. Это значит что нельзя выбрать непрямой потомок (например select=»table tr» ).

Мастер Йода рекомендует:  Знакомство с RxSwift примеры кода реактивного программирования на языке Swift

Ответ: электронный адрес Сергея выводится один раз, в желтом цвете.

Причиной этому то, что создание дерева для контента, который впоследствии будет отображаться на экране, напоминает организацию большого праздника. Элемент content — это приглашение, которое впускает контент из кода страницы в служебные помещения теневой модели документа для последующего отображения на странице. Эти приглашения доставляются по очереди; приглашение получает тот, кто прописан как получатель (с помощью атрибута select ). Контент получает приглашение и отправляется на праздник. Если на тот же адрес отправлено ещё одно приглашение, то оно никого не застаёт дома, и никто больше не придёт на ваш праздник.

Если какой-то элемент не приглашён ни на один праздник, он не отображается вовсе. Вот что случилось с текстом «Здравствуй, мир» в самом первом примере. Это может пригодиться, когда нужно достичь кардинально отличающегося отображения: пропишите семантическую модель в коде страницы, которая будет доступна скриптам, но не будет отражаться, и с помощью JavaScript подключите её к абсолютно иной модели отображения в теневом дереве.

Например, в HTML предложена прекрасная возможность выбора даты. Если прописать , получите аккуратный всплывающий календарь. Но что если нужно предоставить пользователю возможность выбрать диапазон дат для отпуска его мечты? Код страницы нужно прописать так:

и создать теневое дерево, в которое будет помещена таблица для создания первоклассного календаря с подсветкой диапазона дат и другими возможностями. Когда пользователь кликает по дням в календаре, компонент обновляет состояние полей ввода startDate и endDate ; когда пользователь отсылает введённые данные на обработку, отсылаются значения этих элементов input .

Зачем я добавил теги label , если они не будут отображаться? Если пользователь будет просматривать форму в браузере, не поддерживающем теневую модель документа, форма все равно будет работать, хоть и не будет выглядеть так же красиво. Пользователь увидит нечто вроде:

Конец первой части

Это всё основы теневой модели документа, вы успешно ознакомились с первой частью цикла статей! С помощью теневой модели документа можно осуществить намного большее, например, можно применить несколько теней для одного ведущего элемента теневого дерева, или вложенные тени для инкапсуляции, или же построить свою страницу на основе модельно-управляемых представлений (Model-Driven Views) и теневой модели документа. И теневой моделью документа веб-компоненты не ограничиваются. Например, используя ещё одну часть веб-компонентов, под названием «настраиваемые элементы», можно прописать теневое дерево для виджетов декларативно вместо того, чтобы использовать для этого скрипты.

Как работает JS: технология Shadow DOM и веб-компоненты

Сегодня, в переводе 17 части материалов, посвящённых особенностям всего, что так или иначе связано с JavaScript, речь пойдёт о веб-компонентах и о различных стандартах, которые направлены на работу с ними. Особое внимание здесь будет уделено технологии Shadow DOM.

Обзор

Веб-компоненты — это семейство API, предназначенных для описания новых элементов DOM, подходящих для повторного использования. Функционал таких элементов отделён от остального кода, их можно применять в веб-приложениях собственной разработки.

Существует четыре технологии, относящиеся к веб-компонентам:

  • Shadow DOM (теневой DOM)
  • HTML Templates (HTML-шаблоны)
  • Custom Elements (пользовательские элементы)
  • HTML Imports (HTML-импорт)

В этом материале мы поговорим о технологии Shadow DOM, которая разработана для создания приложений, основанных на компонентах. Она предлагает способы решения распространённых проблем веб-разработки, с которыми вы, возможно, уже сталкивались:

  • Изоляция DOM: компонент обладает изолированным деревом DOM (это означает, что команда document.querySelector() не позволит обратиться к узлу в теневом DOM компонента). Кроме того, это упрощает систему CSS-селекторов в веб-приложениях, так как компоненты DOM изолированы, что даёт разработчику возможность использовать одни и те же универсальные идентификаторы и имена классов в различных компонентах, не беспокоясь о возможных конфликтах имён.
  • Изоляция CSS: CSS-правила, описанные внутри теневого DOM, ограничены им. Эти стили не покидают пределов элемента, они не смешиваются с другими стилями страницы.
  • Композиция: разработка декларативного API для компонентов, основанного на разметке.

Технология Shadow DOM

Тут предполагается, что вы уже знакомы с концепцией DOM и с соответствующими API. Если это не так — можете почитать этот материал.

Shadow DOM — это, в целом, то же самое, что и обычный DOM, но с двумя отличиями:

  • Первое заключается в том, как Shadow DOM создают и используют, в частности, речь идёт об отношении Shadow DOM к остальным частям страницы.
  • Второе заключается в поведении Shadow DOM по отношению к странице.

При работе с DOM создаются узлы DOM, которые присоединяются, в качестве дочерних элементов, к другим элементам страницы. В случае с технологией Shadow DOM создают изолированное дерево DOM, которое присоединяется к элементу, но оно отделено от его обычных дочерних элементов.

Это изолированное поддерево называют shadow tree (теневое дерево). Элемент, к которому присоединено такое дерево, называется shadow host (теневой хост-элемент). Всё, что добавляется в теневое поддерево DOM, оказывается локальным для элемента, к которому оно присоединено, в том числе — стили, описываемые с помощью тегов . Именно так в рамках технологии Shadow DOM обеспечивается изоляция CSS.

Создание Shadow DOM

Shadow root (теневой корневой элемент) — это фрагмент документа, который присоединяется к хост-элементу. Элемент обзаводится теневым DOM тогда, когда к нему присоединяют теневой корневой элемент. Для того, чтобы создать для некоего элемента теневой DOM, нужно воспользоваться командой вида element.attachShadow() :

Надо отметить, что в спецификации Shadow DOM имеется список элементов, к которым нельзя подключать теневые поддеревья DOM.


Композиция в Shadow DOM

Композиция — это одна из важнейших возможностей Shadow DOM, это способ создания веб-приложений, который применяется в процессе написания HTML-кода. В ходе этого процесса программист комбинирует различные строительные блоки (элементы), из которых состоит страница, вкладывая их, при необходимости, друг в друга. Например, это такие элементы, как

Сила и мощь веб-компонентов

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

Для использования стороннего кода требуются шаблонные вставки на JavaScript или решение CSS-конфликтов с участием ужасного !important . В мире React и других современных фреймворков дела обстоят чуть лучше, но задействовать целый фреймворк только для того, чтобы использовать готовый виджет, будет излишним.

HTML5 добавил несколько новых элементов типа и , которые позволили внедрить в веб-платформу некоторые востребованные виджеты общего назначения. Тем не менее, добавление в стандарт нового элемента для каждого достаточно распространённого UI-паттерна не выглядит подходящим вариантом.

В ответ на проблему был подготовлен небольшой набор стандартов. Каждый стандарт имеет частное независимое применение, но в совокупности они открывают то, что ранее было невозможно реализовать напрямую и чрезвычайно сложно сымитировать. А именно поддержку пользовательских HTML-элементов, которые можно встроить в те же места, что и традиционный HTML. Более того, эти элементы скрывают свою внутреннюю сложность от сайта, на котором они используются, аналогично сложным формам ввода или видеоплееру.

Развитие стандартов

Упомянутый набор стандартов известен как веб-компоненты. Ранние версии стандартов в той или иной форме доступны в Chrome ещё с 2014, а полифиллы неуклюже заполняют пробелы в других браузерах.

Не так давно стандарты были усовершенствованы. Первоначальная версия теперь называется нулевой — v0, а для более зрелой первой версии виднеется поддержка во всех основных браузерах. В Firefox 63 добавлена поддержка двух стандартов из требуемых — Custom Elements и Shadow DOM — так что пора узнать, как можно стать HTML-изобретателем!

Разговоры о веб-компонентах идут давно, и существует множество доступных материалов по ним. Статью следует рассматривать как вводную по новым возможностям и ресурсам. Если вы хотите окунуться в тему глубже (а это определённо следует сделать), то можно почитать о веб-компонентах на MDN Web Docs и Google Developers.

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

Новый взгляд на элемент

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

Но куда вставляется разобранный HTML, если не в документ? Он добавляется в DocumentFragment — легковесную обёртку для хранения фрагмента HTML-документа, которая «растворяется» при вставке в DOM. Эти обёртки полезны для хранения набора элементов, требуемых позже, и выступают в роли контейнера, которым не нужно управлять вручную.

Теперь у нас есть DOM в неком «исчезающем» контейнере, а как им пользоваться?

Оказывается, можно просто вставить фрагмент в текущий документ:

Этот код работает прекрасно за тем исключением, что вы только что удалили контейнер. Если вы запустите код ещё раз, то получите ошибку, так как template.content исчез. Вместо этого сделайте копию фрагмента перед вставкой:

Название метода cloneNode говорит само за себя, но обратите внимание, что он принимает флаг для указания необходимости глубокого копирования.

Тег template подходит в любой ситуации, когда требуется повторять некоторую HTML-структуру. Он особенно полезен для задания внутренней структуры компонента, и потому входит в «клуб» веб-компонентов.

Новые возможности:

  • Элемент, который хранит HTML-код, но не добавляет его в текущий документ.

Обзорные материалы:

  • DocumentFragment
  • Дублирование узлов DOM с помощью cloneNode

Пользовательские элементы (Custom Elements)

Пользовательские элементы — флагман технологии веб-компонентов. Название говорящее — они позволяют разработчикам создать свои собственные HTML-элементы. Синтаксис версии v0 был громоздким, но сейчас пользовательские элементы в основном опираются на классы ES6. Если вы с ними знакомы, то знаете, как описать новый класс на базе существующего (наследование):

А что, если мы сделаем так?

До недавнего времени это считалось ошибкой. Браузеры запрещали наследовать как встроенный класс HTMLElement , так и любой из его потомков. Custom Elements снимают данное ограничение.

Браузер знает, что тегу

соответствует класс HTMLParagraphElement , но как он поймет, какой тег сопоставлен пользовательскому элементу? В дополнение к возможности наследования встроенных классов для установки такого соответствия доступно Registry пользовательских элементов:

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

Но зачем в имени тега нужен дефис? Органы стандартизации хотят сохранить себе возможность вводить новые HTML-элементы, так что разработчики не могут просто так начать определять свои теги или . Во избежание в будущем конфликтов все пользовательские элементы должны содержать дефис, а органы стандартизации обещают никогда не вводить новые HTML-теги с этим символом. Проблема решена!

Кроме конструктора существует ряд дополнительных методов, связанных с жизненным циклом элемента. Обращение к ним происходит в различные моменты:

  • connectedCallback вызывается при добавлении элемента в документ (в общем случае несколько раз, так как элемент может быть перемещён или удалён и заново добавлен);
  • disconnectedCallback — в противоположность connectedCallback ;
  • attributeChangeCallback срабатывает при модификации атрибутов из специального списка.

Немного более выразительный пример:

Использование элемента на странице выглядит так:


А что, если вы хотите расширить функциональность существующего HTML-элемента? Вы можете и определённо должны так делать, но использование таких элементов в разметке выглядит совсем иначе. Допустим, наше приветствие будет кнопкой:

Также необходимо указать в Registry, что мы расширяем существующий тег:

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

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

С этого момента уже можно применять традиционные техники веб-виджетов. Можно установить ряд обработчиков событий, стилизовать элемент и даже повторять его внутреннюю структуру через . Люди могут комбинировать ваш пользовательский элемент с их собственным кодом: через HTML-шаблонизацию, работу с DOM и даже в новомодных фреймворках, некоторые из которых поддерживают пользовательские теги в своих реализациях виртуального DOM. Пользовательские элементы используют стандартные интерфейсы, поэтому они позволяют реализовать действительно переносимые виджеты.

Новые возможности:

  • Наследование встроенного класса HTMLElement и его подклассов.
  • Registry пользовательских элементов и customElements.define() для работы с ним.
  • Специальные методы жизненного цикла для реагирования на создание элемента, вставку в DOM, изменений атрибутов и так далее.

Обзорные материалы:

  • ES6 Classes, в частности, наследование и ключевое слово extends

Теневая модель документа (Shadow DOM)

Мы сделали свой «дружелюбный» элемент и даже накинули немного стилизации. Теперь мы хотим использовать его на всех своих сайтах и делиться кодом с другими. Как предотвратить кошмар конфликтов, когда наш кастомизированный элемент столкнётся лицом к лицу с CSS других сайтов? Решение предоставляет Shadow DOM (теневой DOM).

Стандарт теневого DOM вводит концепцию теневого корня. Говоря поверхностно, теневой корень поддерживает стандартные DOM-методы и может быть добавлен так же, как и любой другой DOM-узел. Теневой корень отличается тем, что его родительский узел не видит содержимое:

Содержимое теневого корня стилизуется через добавление к нему (или
):

Ух, а прямо сейчас мы действительно можем использовать ! В любом случае, элемент будет изменяем со стороны стилей внутри корня, а для всех внешних стилей — нет.

Что, если пользовательский элемент имеет нетеневой контент? Мы можем заставить их работать вместе, используя новый специальный элемент :

Если этот template будет прикреплен к теневому корню, то следующая разметка:

Будет отображена так:

Возможность соединения теневого корня с нетеневым контентом позволяет создавать продвинутые пользовательские элементы со сложной внутренней структурой, но простые с точки зрения внешнего окружения. Слоты более функциональны, чем здесь показано: можно использовать несколько слотов и специальные CSS-псевдоклассы для выбора их содержимого.

Новые возможности:

  • Как бы «невидимая» DOM-структура, которая называется теневым корнем.
  • DOM APIs для создания теневых корней и доступа к ним.
  • Ограничение области видимости внутренних стилей теневого корня.
  • Новые псевдоклассы CSS для работы с теневыми корнями и их стилями.
  • Элемент

Собираем всё вместе

Давайте сделаем модную кнопку! Будем креативны и назовём элемент . Что именно сделает кнопку модной? У неё будут кастомные стили, которые позволят нам установить иконку и настроить внешний вид. Хотелось бы, чтобы вид кнопки сохранялся вне зависимости от сайта, на котором она используется, так что мы собираемся инкапсулировать стили в теневом корне.

Вы можете увидеть законченный пользовательский элемент в интерактивном примере. Обязательно посмотрите как на JS-код, так и на в HTML, который нужен для определения стиля и структуры элемента.

Заключение

Стандарты веб-компонентов основаны на той идее, что имея множество низкоуровневых средств, люди будут комбинировать их способами, которые никто не предвидел во время написания спецификаций. Пользовательские элементы уже были использованы для упрощения создания VR-контента в вебе, породили несколько UI-тулкитов и многое другое. Несмотря на длительный процесс стандартизации, обещанные возможности веб-компонентов придают разработчикам больше сил. Теперь, когда технология доступна в браузерах, будущее веб-компонентов в ваших руках. Что вы построите?

Введение в Shadow DOM

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

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

Мастер Йода рекомендует:  Как руководителю не из IT-сферы управлять айтишниками в компании — отвечают эксперты


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

Возвращаясь к нашей проблеме, скажу, что мы безусловно можем инкапсулировать код 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.

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

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

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

В инспекторе показывается внутренняя организация аудиоплеера, которую без активации показа 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:

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

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

  • 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 оно меняется так, чтобы целевой элемент поменялся на элемент

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

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

    После отображения страницы контент Shadow Host проецируется в место, где появляется элемент (* ранее использовался в Shadow DOM в качестве точки вставки. Не предполагалось, что он будет использоваться в обычном HTML. Заменен элементом , что используется для создания точки в DOM, куда может быть вставлен Shadow DOM). Это очень упрощенный пример, в котором в при композиции помещается все, что находится в 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) для виджета, и просто используете этот виджет в вашей разметке в виде . Таким образом, пользовательские элементы по существу предоставляют нам способ объединить все возможности 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 отображается в браузере.

    What the Heck is Shadow DOM?

    If you build Web sites, you probably use Javascript libraries. If so, you are probably grateful to the nameless heroes who make these libraries not suck.

    One common problem these brave soldiers of the Web have to face is encapsulation. You know, one of them turtles on which the Object-Oriented Programming foundation sits, upon which stands most of the modern software engineering. How do you create that boundary between the code that you wrote and the code that will consume it?

    With the exception of SVG (more on that later), today’s Web platform offers only one built-in mechanism to isolate one chunk of code from another — and it ain’t pretty. Yup, I am talking about iframes. For most encapsulation needs, frames are too heavy and restrictive.

    What do you mean I must put each of my custom buttons in a separate iframe? What kind of insane are you?

    So we need something better. Turns out, most browsers have been sneakily employing a powerful technique to hide their gory implementation details. This technique is called the shadow DOM.

    My name is DOM, Shadow DOM

    Shadow DOM refers to the ability of the browser to include a subtree of DOM elements into the rendering of a document, but not into the main document DOM tree. Consider a simple slider:

    Pop this code into any WebKit-powered browser, and it’ll appear like so:

    Simple enough. There’s a slider track and there’s a thumb, which you can slide along the track.

    Wait, what? There’s a separate movable element inside of the input element? How come I can’t see it from Javascript?

    Is this some sort of magic?

    No magic, my fair person of the Web. Just shadow DOM in action. You see, browser developers realized that coding the appearance and behavior of HTML elements completely by hand is a) hard and b) silly. So they sort of cheated.

    They created a boundary between what you, the Web developer can reach and what’s considered implementation details, thus inaccessible to you. The browser however, can traipse across this boundary at will. With this boundary in place, they were able to build all HTML elements using the same good-old Web technologies, out of the divs and spans just like you would.

    Some of these are simple, like the slider above. Some get pretty complex. Check out the video element. It’s got trigger buttons, timelines, a hover-appearing volume control, you name it:

    All of this is just HTML and CSS — hidden inside of a shadow DOM subtree.

    To borrow a verse from that magnetic meme duo, “how does it work?” To build a better mental model, let’s pretend we have a way to poke at it with Javascript. Given this simple page:

    We get the DOM tree like this:

    But it is rendered as if it were this:

    Or visually like so:

    Notice how the second part of the rendered sentence is not green? That’s because the p selector I have in my document can’t reach into the shadow DOM. How cool is that?! What would a framework developer give to have powers like this? The ability to write your widget and not worry about some random selector fiddling with your style seems … downright intoxicating.

    Мастер Йода рекомендует:  Изучаем Angular текстовые туториалы

    Course of Events

    To keep things natural, events fired in shadow DOM subtree can be listened to in the document. For instance, if you click on the mute button in the audio element, your event listeners on an enclosing div would hear the click:

    However, if you ask to identify who fired the event, you’ll find out it was the audio element itself, not some button inside of it.

    Why? Because when crossing the shadow DOM boundary, the events are re-targeted to avoid exposing things inside of the shadow subtree. This way, you get to hear the events, fired from the shadow DOM, and the implementor gets to keep their details hidden from you.

    Reaching into Shadows with CSS

    One other trick up the sleeve is the ability to control how and whether CSS reaches into the shadow subtree. Suppose I want to customize the look of my slider. Instead of the standard OS-specific appearance, I want it be stylish, like so:


    The result I get is:

    Ok, that’s nice, but how do I style the thumb? We already determined the that our usual selectors don’t go into the shadow DOM tree. Turns out, there’s a handy pseudo attribute capability, which allows shadow DOM subtrees to associate an arbitrary pseudo-element identifier with an element in the subtree. For example, the thumb in the WebKit slider can be reached at:

    Ain’t it great? Think about it. You can style elements in the shadow DOM without actually being able to access them. And the builder of the shadow DOM subtree gets to decide which specific parts of their tree can be styled. Don’t you wish you had powers like this when building your UI widget toolkit?

    Shadows with Holes, How’s that for a Mind-bender?

    Speaking of awesome powers, what happens when you add a child to an element with a shadow DOM subtree? Let’s experiment:

    Whoa. Welcome to the twilight DOM — a chunk of document that’s accessible by traversal but not rendered on the page. Is it useful? Not very. But it’s there for you, if you need it. Teens seem to like it.

    But what if we did have the ability to show element’s children as part of its shadow DOM subtree? Think of the shadow DOM as a template with a hole, through which the element’s children peek:

    As a result, if you traverse the DOM you will see this:

    But it will render like this:

    As you add children to element , they act as normal children if you look at them from the DOM, but rendering-wise, they are teleported into a hole in the shadow DOM subtree.

    This is the point where you admit that this is pretty cool and start asking:

    When can I have it in my browser?

    Homework Assignment

    Did you think you’d read through all this preaching and get away without homework? As a Javascript library or framework developer, try to think of all the different great things having shadow DOM would allow you to do. Then think of specific use cases (actual/pseudo code a plus) of where shadow DOM could be applied. To help you get your thinking groove going, here is current list of use cases.

    Finally. share your use cases on public-webapps mailing list. The discussion about adding these capabilities to the Web platform is under way and your help is needed.

    If you aren’t a much a framework writer, you can still participate — by cheering for the shadow DOM and spreading the joy on your favorite social networking site. Because joy is what’s it’s all about.

    PS. SVG and Shadow DOM

    Almost forgot. Believe it or not, SVG has actually had shadow DOM since the beginning. The trouble is, its shadow DOM is very… shady. No, that’s not it. There’s another qualifier that also begins with “sh” and ends with a “y”. Yeah, that one. I could go on, but trust me on this. Or read the spec.

    Using shadow DOM

    On this Page

    An important aspect of web components is encapsulation — being able to keep the markup structure, style, and behavior hidden and separate from other code on the page so that different parts do not clash, and the code can be kept nice and clean. The Shadow DOM API is a key part of this, providing a way to attach a hidden separated DOM to an element. This article covers the basics of using the Shadow DOM.

    Note: Shadow DOM is supported by default in Firefox (63 and onwards), Chrome, Opera, and Safari. The new Chromium-based Edge (75 and onwards) supports it too; the old Edge didn’t.

    High-level view

    This article assumes you are already familiar with the concept of the DOM (Document Object Model) — a tree-like structure of connected nodes that represents the different elements and strings of text appearing in a markup document (usually an HTML document in the case of web documents). As an example, consider the following HTML fragment:

    This fragment produces the following DOM structure:

    Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which can be attached to any elements you want, in the same way as the normal DOM.

    There are some bits of shadow DOM terminology to be aware of:

    • Shadow host: The regular DOM node that the shadow DOM is attached to.
    • Shadow tree: The DOM tree inside the shadow DOM.
    • Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
    • Shadow root: The root node of the shadow tree.

    You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a element contains style information for a document, or part of a document.»> element. The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.

    Note that the shadow DOM is not a new thing by any means — browsers have used it for a long time to encapsulate the inner structure of an element. Think for example of a ) embeds a media player which supports video playback into the document. You can use for audio content as well, but the element may provide a more appropriate user experience.»> element, with the default browser controls exposed. All you see in the DOM is the element, but it contains a series of buttons and other controls inside its shadow DOM. The shadow DOM spec has made it so that you are allowed to actually manipulate the shadow DOM of your own custom elements.

    Basic usage

    You can attach a shadow root to any element using the Element.attachShadow() method. This takes as its parameter an options object that contains one option — mode — with a value of open or closed :

    open means that you can access the shadow DOM using JavaScript written in the main page context, for example using the Element.shadowRoot property:

    If you attach a shadow root to a custom element with mode: closed set, you won’t be able to access the shadow DOM from the outside — myCustomElem.shadowRoot returns null . This is the case with built in elements that contain shadow DOMs, such as .

    Note: As this blog post shows, it is actually fairly easy to work around closed shadow DOMs, and the hassle to completely hide them is often more than it’s worth.

    If you are attaching a shadow DOM to a custom element as part of its constructor (by far the most useful application of the shadow DOM), you would use something like this:

    When you’ve attached a shadow DOM to an element, manipulating it is a matter of just using the same DOM APIs as you use for the regular DOM manipulation:

    Working through a simple example

    Now let’s walk through a simple example to demonstrate the shadow DOM in action inside a custom element —

    (see a live example also). This takes an image icon and a text string, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information. To begin with, in our JavaScript file we define a class called PopUpInfo , which extends HTMLElement :

    Inside the class definition we define the element’s constructor, which defines all the functionality the element will have when an instance of it is instantiated.


    Creating the shadow root

    We first attach a shadow root to the custom element:

    Creating the shadow DOM structure

    Next, we use some DOM manipulation to create the element’s internal shadow DOM structure:

    Styling the shadow DOM

    After that we create a element contains style information for a document, or part of a document.»> element and populate it with some CSS to style it:

    Attaching the shadow DOM to the shadow root

    The final step is to attach all the created elements to the shadow root:

    Using our custom element

    Once the class is defined, using the element is as simple as defining it, and putting it on the page, as explained in Using custom elements:

    Internal versus external styles

    For example, take a look at this code from our popup-info-box-external-stylesheet example (see the source code):

    Many modern browsers implement an optimization for element contains style information for a document, or part of a document.»> tags either cloned from a common node or that have identical text, to allow them to share a single backing stylesheet. With this optimization the performance of external and internal styles should be similar.

    Заменят ли Web Components фреймворки?

    Веб-разработка стремительно развивается и появляются новые виды различных инструментов разработки: библиотеки и фреймворки. Их появление обусловлено необходимостью быстрой и качественной разработки веб-приложений и получения результата, которого javascript в чистом виде не всегда способен предоставить. Но и реализация новых методов, которые могут сделать разработку эффективней, продолжает происходить. Благодаря этому, хоть и не такому быстрому развитию, как хотелось бы, появились специальные веб-компоненты, которые обещают помогать делать обычными средствами javascript и браузеров то, что раньше можно было сделать только подключив определенную библиотеку или использовав фреймворк.

    Web Components (Веб-компоненты) — это технология позволяющая создавать компоненты в веб-документах и веб-приложениях для многократного использования.

    Веб-компоненты частично поддерживаются в браузерах Chrome, Firefox, Opera и Safari и работают напрямую без необходимости подключать какие-либо библиотеки. В остальных, где нет поддержки веб-компонентов, используется полифилы.

    Полифилы — это код, реализующий функционал, которые не поддерживается по умолчанию в некоторых браузерах. Полифил является специальной библиотекой, которая подключается для поддержки стандартов HTML 5.

    Веб-компоненты включают 4 технологии:

    • Custom Elements — API для создания собственных HTML элементов.
    • HTML Templates — тег для реализации изолированные DOM-элементов
    • Shadow DOM — изолирует DOM и стили в разных элементах.
    • HTML Imports — импорт HTML документов.

    Спецификация Web Components располагаются в GitHub репозитории webcomponents.

    Спецификация веб-компонентов определяет следующие пункты:

    • Новые элементы HTML:
    • Связанные интерфейсы API для новых элементов: HTMLTemplateElement, HTMLContentElement (удален из спецификации) и HTMLShadowElement (удален из спецификации)
    • Расширения для интерфейса HTMLLinkElement и
    • API для регистрации встроенных элементов, Document.registerElement(), и модификации Document.createElement() и Document.createElementNS()
    • Новый «жизненный цикл колбеков» может быть добавлен в прототип, на основе которого создается встроенный элемент
    • Новый CSS псевдо-класс для задания стилей по умолчанию, :unresolved.
    • Теневой DOM: ShadowRoot и Element.createShadowRoot(), Element.getDestinationInsertionPoints(), Element.shadowRoot
    • Расширение для интерфейса Event, Event.path
    • Расширение для интерфейса Document
    • Для задания стилей Веб-компонент:
      • Новые псевдо-классы: :host, :host(), :host-context()
      • Новые псевдо-элементы: ::shadow и ::content
      • Новый комбинатор, /deep/.

    Custom Elements

    Custom Elements (Пользовательские элементы) — эта технология позволяет создавать свои типы элементов, описывать для них свойства, методы и т.п.

    Для регистрации нового элемента используется конструкция:

    • name — имя нового элемента (тега). В названии обязательно должен быть дефис. Это сделано, чтобы предотвратить конфликт со стандартными HTML-элементами. Например, нальзя создать элемент footertop или footerTop, а название должно быть footer-top
    • prototype — объект-прототип нового элемента, который должен наследовать от HTMLElement, чтобы у нового элемента были стандартные методы и свойства.

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

    Преимущество, которое имеют Custom Elements, — это их жизненные реакции , которые позволяют привязывать поведение к различным частям «жизненного цикла» нового элемента. Например, можно установить определенное поведение, когда элемент вставлен в DOM («подключен»), и другое поведение, когда оно удаляется из DOM («отключено») или когда его атрибуты меняются.

    Методы Custom Elements

    Пользовательские элементы имеют следующие методы, которые определяют, как они себя ведут:

    • constructor() — вызывается, когда элемент создается или обновляется
    • connectedCallback() — вызывается, когда элемент вставлен в документ, в том числе в теневое дерево
    • disconnectedCallback() — вызывается, когда элемент удаляется из документа
    • attributeChangedCallback(attributeName, oldValue, newValue, namespace) — вызывается, когда атрибут изменяется, добавляется, удаляется или заменяется элементом.
    • adoptedCallback(oldDocument, newDocument) — вызывается, когда элемент принят в новый документ

    Пример Custom Elements

    HTML Templates

    HTML Templates — это отложенный рендер клиентского контента, который не отображается во время загрузки, но может быть инициализирован при помощи JavaScript. Парсер обрабатывает содержимое элемента во время загрузки страницы, только лишь чтобы убедиться в валидности содержимого, не отображая при этом само содержимое.

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

    Пример HTML Templates

    Shadow DOM

    Shadow DOM — это инкапсуляция для DOM и CSS в веб-компоненте и делает так, чтобы они оставались отдельно от DOM основного документа.
    Суть инкапсуляции в том, чтобы сохранить код отдельно от остальной части страницы, когда, например, на большом сайте, не очень хорошо организован CSS и стили могут применяться в основной области содержимого, где этого делать не требуется, и наоборот.

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

    Теневая DOM добавляется с помощью Element.attachShadow().

    Пример Shadow DOM

    Shadow DOM состоит из следующих частей:

    • Element.attachShadow()
    • Element.getDestinationInsertionPoints()
    • Element.shadowRoot
    • элемент
    • Элемент
    • Связанные интерфейсы API: ShadowRoot, HTMLTemplateElementиHTMLSlotElement
    • Селекторы CSS:
      • Псевдо-классы: :host, :host(),:host-context()
      • Псевдо-элементы: ::slotted()
      • Комбинатор: >>>*

    Интерфейсы Shadow DOM

    • ShadowRoot — корневой узел поддерева DOM, который отображается отдельно от основного дерева DOM документа.
    • HTMLTemplateElement — включает доступ к содержимому HTML- элемента.
    • HTMLSlotElement — включает доступ к имени и назначенным узлам элемента HTML .
    • DocumentOrShadowRoot — предоставляет API, которые совместно используются документами и теневыми корнями.

    HTML Imports

    HTML Imports (HTML импорт) — это механизм упаковки для веб-компонентов, но его также можно использовать сам по себе.

    Импорт производится с помощью тега
    :

    Полноценные веб-компоненты будут не скоро. Кроме этого, как и все остальное в javascript, в чистом виде редко используется и применяются различные фреймворки и библиотеки, являющиеся обертками вокруг этих чистых методов для более простого использования.

    Так что, можно ожидать, что веб-компоненты не заменят фреймворки и библиотеки, а войдут в их текущий функционал и будут сосуществовать наравне с ними. По крайней мере, чтобы заменить фреймворки им придется потягаться с ними. Да и, в общем-то, сами стандарты веб-компоненто, еще не до конца определены.

    Как вы думаете, заменят ли Web Components фреймворки?

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