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

100-4452420

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

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

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

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

Начнем с

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

The Zombies are coming!

123   

The Zombies are coming!

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

Элемент

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

The Zombies are coming!

123   

The Zombies are coming!

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

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

The Zombies are coming!

123   

The Zombies are coming!

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

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

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

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

Halitosis Laden Undead Minions

The Zombies are coming!

1234567   Halitosis Laden Undead Minions   

The Zombies are coming!

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

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

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

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

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

JavaScript // Определяет элемент с нашим именем,

customElements.define(“apocalyptic-warning”,

// Гарантирует, что у нас есть все свойства и методы по умолчанию встроенного элемента HTML
class extends HTMLElement {

// Вызывается каждый раз, когда создается новый пользовательский элемент
constructor() {

// Вызывает родительский конструктор, то есть конструктор для элемента HTML, чтобы все было настроено точно так же, как и при создании встроенного HTML-элемента.
super();

// Берёт и сохраняет его в `warning`
let warning = document.getElementById(“warningtemplate”);

// Сохраняет содержимое шаблона в `mywarning`
let mywarning = warning.content;

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

});

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

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

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

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

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

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

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

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

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

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

p { background-color: pink; padding: 0.5em; border: 1px solid red; }

The Zombies are coming!

1234567891011       p {      background-color: pink;      padding: 0.5em;      border: 1px solid red;    }       

The Zombies are coming!

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

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

10-6906786

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

CSS apocalyptic-warning span { color: blue;

}

123 apocalyptic-warning span {  color: blue;}

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

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

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

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

JavaScript customElements.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)); } }

);

Искусственный интеллекту упростит работу с данными в Google Таблицах

12345678910 customElements.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:

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 ‘; }

””

Zombie Bob

Age: 37
Infection Date: September 12, 2025

Interests:

  • Long Walks on Beach
  • brains
  • defeating humanity

Apocalyptic Statement: Moooooooan!

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566       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 ‘;    }         ””         

Zombie Bob

     Age: 37    Infection Date: September 12, 2025           Interests:               

              

  • Long Walks on Beach
  •           

  • brains
  •           

  • defeating humanity
  •         

               Apocalyptic Statement: Moooooooan!   

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

CSS zombie-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 ‘;

}

12345678910111213141516171819202122232425262728 zombie-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, Яндекс.Дзен

Источник