Веб-компоненты проще, чем вы думаете

Веб-компоненты проще, чем вы думаете

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

Но в недавнем справочном проекте, призванном упростить изучение HTML (конечно, путем проб и ошибок), я решил, что мне нужно познакомится с каждым элементом HTML в спецификации. Помимо этих конференций, это было мое первое знакомство с элементами <slot> и <template>. Но для написания чего-нибудь простенького и увлекательного, пришлось копнуть немного глубже.

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

Я здесь, чтобы сказать вам, что вы — да, вы — можете создать веб-компонент. Давайте на мгновение оставим наши отвлекающие факторы и страхи, давайте сделаем это вместе.

Начнем с <template>

A <template> — это элемент HTML, который позволяет нам создать, шаблон — структуру HTML для веб-компонента. Шаблон не обязательно должен быть огромным фрагментом кода. Он может быть очень простой:

123<template>  <p>The Zombies are coming!</p></template>

Элемент <template> имеет важное значение, потому что он связывает все вместе. Это как фундамент здания; это база, на которой построено все остальное. Давайте использовать этот небольшой фрагмент HTML в качестве шаблона для нашего веб-компонента <apocalyptic-warning> — в качестве предупреждения, когда на нас надвигается зомби-апокалипсис.

Элемент <slot>

<slot> это просто еще один элемент HTML, как и <template>. Но в этом случае <slot> настраивает то, как <template> отображается на странице.

123<template>  <p>The <slot>Zombies</slot> are coming!</p></template>

Здесь мы вставили слово «Zombies» в шаблонную разметку. Если мы ничего не делаем с тегом slot, по умолчанию используется содержимое между тегами.

Использование slot очень похоже на заполнитель. Мы можем использовать заполнитель как есть или вместо него указать что-нибудь другое. Мы делаем это с помощью атрибута name.

123<template>  <p>The <slot name=”whats-coming”>Zombies</slot> are coming!</p></template>

Атрибут name сообщает о веб-компоненте, содержание которого описано в шаблоне. Прямо сейчас у нас есть слот под названием whats-coming. Мы предполагаем, что зомби придут первыми в апокалипсисе, но этот slot дает нам некоторую гибкость, чтобы вставить что-то еще, например, если это будет робот, оборотень или даже апокалипсисом веб-компонентов.

READ
Преобразование объекта с фиксированными размерами в адаптивный элемент

Использование компонента

Технически мы закончили «писать» компонент и можем вставить его в любое место, где захотим.

1234567<apocalyptic-warning>  <span slot=”whats-coming”>Halitosis Laden Undead Minions</span></apocalyptic-warning> <template>  <p>The <slot name=”whats-coming”>Zombies</slot> are coming!</p></template>

Видите, что мы там сделали? Мы поместили компонент <apocalyptic-warning> на страницу точно так же, как любой другой div или что-то еще. Но мы также добавили в span ссылку на name атрибут нашего slot. И то, что между ними, в span мы можем заменить на «Zombies» при рендеринге компонента.

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

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

Регистрация компонента

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

JavaScript

123456789101112131415161718192021// Определяет элемент с нашим именем, <apocalyptic-warning>customElements.define(“apocalyptic-warning”,     // Гарантирует, что у нас есть все свойства и методы по умолчанию встроенного элемента HTML   class extends HTMLElement {     // Вызывается каждый раз, когда создается новый пользовательский элемент    constructor() {        // Вызывает родительский конструктор, то есть конструктор для элемента  HTML, чтобы все было настроено точно так же, как и при создании встроенного HTML-элемента.      super();       // Берёт  <template>  и сохраняет его в  `warning`      let warning = document.getElementById(“warningtemplate”);       // Сохраняет содержимое шаблона в `mywarning`      let mywarning = warning.content;       const shadowRoot = this.attachShadow({mode: “open”}).appendChild(mywarning.cloneNode(true));    }  });

Я привёл подробные комментарии, которые объясняют все построчно. Кроме последней строки:

JavaScript

1const shadowRoot = this.attachShadow({mode: “open”}).appendChild(mywarning.cloneNode(true));

Мы здесь много делаем. Во-первых, мы берем наш custom element (this) и создаем скрытого агента — я имею в виду теневой DOM. mode: open просто означает, что JavaScript извне :root может обращаться к элементам в теневой DOM и манипулировать ими, что-то вроде настройки доступа к компоненту через черный ход.

READ
Полноэкранное меню на CSS с эффектом анимации

Была создана теневая DOM, и мы добавили к ней узел. Этот узел есть полной копией шаблона, включая все элементы и текст шаблона. Из шаблона, прикрепленного к теневому DOM пользовательского элемента, подбирается содержимое атрибута slot и подставляется на свое место.

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

Стилизация компонента

Возможно, вы заметили стиль в этой демонстрации. Как и следовало ожидать, у нас есть абсолютная возможность стилизовать наш компонент с помощью CSS. Фактически, мы можем включить элемент style прямо в template.

1234567891011<template id=”warningtemplate”>  <style>    p {      background-color: pink;      padding: 0.5em;      border: 1px solid red;    }  </style>     <p>The <slot name=”whats-coming”>Zombies</slot> are coming!</p></template>

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

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

Веб-компоненты проще, чем вы думаете

А поскольку содержимое технически находится за пределами шаблона, любые дочерние селекторы или классы, которые мы используем в элементе шаблона style, не будут влиять на содержимое, размещенное в slot. Это не позволяет обеспечить полную инкапсуляцию, как я ожидал. Но поскольку пользовательский элемент является элементом, мы можем использовать его в качестве селектора элемента в любом файле CSS, включая основную таблицу стилей, используемую на странице. И хотя вставленный материал технически не находится в шаблоне, он находится в пользовательском элементе, и селекторы потомков из CSS будут работать.

CSS

123apocalyptic-warning span {  color: blue;}

Но будьте осторожны! Стили в основном файле CSS не могут получить доступ к элементам в template теневой модели DOM.

Соберем все это вместе

Давайте посмотрим на пример, скажем, профиль для службы знакомств с зомби, вроде того, который вам может понадобиться после апокалипсиса. Чтобы стилизовать и содержимое по умолчанию и любой вставленный контент, нам нужен элемент style в template и стили в файле CSS.

Код JavaScript точно такой же, за исключением того, что сейчас мы работаем с другим именем компонента, <zombie-profile>.

JavaScript

READ
Искусственный интеллекту упростит работу с данными в Google Таблицах
12345678910customElements.define(“zombie-profile”,  class extends HTMLElement {    constructor() {      super();      let profile = document.getElementById(“zprofiletemplate”);      let myprofile = profile.content;      const shadowRoot = this.attachShadow({mode: “open”}).appendChild(myprofile.cloneNode(true));    }  });

Вот шаблон HTML, включая инкапсулированный CSS:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566<template id=”zprofiletemplate”>  <style>    img {      width: 100%;      max-width: 300px;      height: auto;      margin: 0 1em 0 0;    }    h2 {      font-size: 3em;      margin: 0 0 0.25em 0;      line-height: 0.8;    }    h3 {      margin: 0.5em 0 0 0;      font-weight: normal;    }    .age, .infection-date {      display: block;    }    span {      line-height: 1.4;    }    .label {      color: #555;    }    li, ul {      display: inline;      padding: 0;    }    li::after {      content: ‘, ‘;    }    li:last-child::after {      content: ”;    }    li:last-child::before {      content: ‘ and ‘;    }  </style>   <div class=”profilepic”>    <slot name=”profile-image”><img src=”https://assets.codepen.io/1804713/default.png” alt=””></slot>  </div>   <div class=”info”>    <h2><slot name=”zombie-name” part=”zname”>Zombie Bob</slot></h2>     <span class=”age”><span class=”label”>Age:</span> <slot name=”z-age”>37</slot></span>    <span class=”infection-date”><span class=”label”>Infection Date:</span> <slot name=”idate”>September 12, 2025</slot></span>     <div class=”interests”>      <span class=”label”>Interests: </span>      <slot name=”z-interests”>        <ul>          <li>Long Walks on Beach</li>          <li>brains</li>          <li>defeating humanity</li>        </ul>      </slot>    </div>     <span class=”z-statement”><span class=”label”>Apocalyptic Statement: </span> <slot name=”statement”>Moooooooan!</slot></span>   </div></template>

Вот CSS для нашего элемента <zombie-profile> и его потомков из нашего основного файла CSS. Обратите внимание на дублирование, чтобы гарантировать, что и замененные элементы, и элементы из шаблона имеют одинаковый стиль.

CSS

12345678910111213141516171819202122232425262728zombie-profile {  width: calc(50% – 1em);  border: 1px solid red;  padding: 1em;  margin-bottom: 2em;  display: grid;  grid-template-columns: 2fr 4fr;  column-gap: 20px;}zombie-profile img {  width: 100%;  max-width: 300px;  height: auto;  margin: 0 1em 0 0;}zombie-profile li, zombie-profile ul {  display: inline;  padding: 0;}zombie-profile li::after {  content: ‘, ‘;}zombie-profile li:last-child::after {  content: ”;}zombie-profile li:last-child::before {  content: ‘ and ‘;}

Теперь все собрано вместе!

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

Это действительно так. Чего вы боитесь больше, веб-компонентов или зомби-апокалипсиса? Я мог бы сказать, что в недалеком прошлом боялся веб-компонентов, но теперь я с гордостью могу сказать, что зомби — единственное, что меня беспокоит.

Автор: John Rhea

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен

Источник

Оценить статью
Блог о самом интересном.
Добавить комментарий