Создание веб-компонента с нуля

Источник: «Let's create a Web Component from scratch!»
Сегодня я решил, что мы создадим HTML веб-компонент с нуля. Давайте создадим компонент, показывающий и скрывающий текст при переключении кнопки.

HTML

Сердце HTML веб-компонента — HTML.

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

Для этого добавим button, показывающий или скрывающий контент, и div с контентом. Поскольку мы не хотим, чтобы кнопка отображалась пока веб-компонент не будет готов, добавим атрибут [hidden], чтобы скрыть её.

<button hidden>Show Content</button>

<div>
<p>Now you see me, now you don't!</p>
</div>

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

Давайте обернём его в пользовательский элемент, который назовём show-hide. Также добавим атрибут [trigger] к кнопке button и [content] к контенту.

Наш веб-компонент использует их после своего определения для определения переключателя и содержимого, соответственно. Это позволяет нам поместить button до или после содержимого и даёт нам некоторую гибкость.

<show-hide>
<button trigger hidden>Show Content</button>

<div content>
<p>Now you see me, now you don't!</p>
</div>
</show-hide>

Определение веб-компонента

Теперь, когда у нас есть базовый HTML, определим наш веб-компонент с помощью метода customElements.define().

Определим наш пользовательский элемент show-hide и расширим класс HTMLElement. В constructor() используем метод super(), чтобы получить доступ к свойствам родительского класса.

customElements.define('show-hide', class extends HTMLElement {

/**
* Определение веб-компонента
*/

constructor () {
// Получение свойств родительского класса
super();
}
});

Определение свойств

Далее давайте определим свойства.

С помощью метода Element.querySelector() найдём элементы [trigger] и [content] внутри пользовательского элемента (this) и присвоим их свойствам trigger и content соответственно.

Если элементов не существует, вызываем return, чтобы закончить настройку раньше времени.

/**
* Определение веб-компонента
*/

constructor () {

// Получение свойств родительского класса
super();

// Получение элементов
this.trigger = this.querySelector('[trigger]');
this.content = this.querySelector('[content]');
if (!this.trigger || !this.content) return;

}

Настройка DOM

Определив свойства, перейдём к работе с DOM и настраивать слушатель событий.

Сначала воспользуемся методом Element.removeAttribute(), для удаления атрибута [hidden] из элемента button, this.trigger.

Также воспользуемся методом Element.setAttribute(), чтобы добавить атрибут [aria-expanded] со значением false. Это сообщит устройствам чтения с экрана, что кнопка переключает видимость содержимого, и каково текущее состояние этого содержимого.

/**
* Определение веб-компонента
*/

constructor () {

// Получение свойств родительского класса
super();

// Получение элементов
this.trigger = this.querySelector('[trigger]');
this.content = this.querySelector('[content]');
if (!this.trigger || !this.content) return;

// Настройка UI по умолчанию
this.trigger.removeAttribute('hidden');
this.trigger.setAttribute('aria-expanded', false);

}

Затем добавим атрибут [hidden] к элементу this.content, чтобы скрыть его.

Затем добавим слушателя события click к элементу this.trigger. Используем метод handleEvent(), встроенный в Web-компоненты, для обработки нашего события (подробнее об этом чуть позже), и передадим this в качестве обратного вызова.

/**
* Определение веб-компонента
*/

constructor () {

// ...

// Настройка UI по умолчанию
this.trigger.removeAttribute('hidden');
this.trigger.setAttribute('aria-expanded', false);
this.content.setAttribute('hidden', '');

// Слушаем событие click
this.trigger.addEventListener('click', this);

}

Обработка событий

Метод handleEvent() — часть API EventListener, и существует уже десятки лет.

Если вы прослушиваете событие с помощью метода addEventListener(), то в качестве второго аргумента можно передать не функцию обратного вызова, а объект.

Пока у этого объекта есть метод handleEvent(), событие будет передано в него, но при этом сохранится привязка к объекту.

customElements.define('show-hide', class extends HTMLElement {

/**
* Определение веб-компонента
*/

constructor () {
// ...
}

/**
* Обработка событий в веб-компоненте
* @param {Event} event объект Event
*/

handleEvent (event) {
// Обработка события...
}

});

Внутри метода handleEvent() сначала запустим метод event.preventDefault(), чтобы убедиться, что кнопка не вызовет никаких неожиданных побочных эффектов, например отправки формы.

/**
* Обработка событий в веб-компоненте
* @param {Event} event объект Event
*/

handleEvent (event) {

// Не позволяем кнопке запускать другие действия
event.preventDefault();

}

Затем воспользуемся методом Element.getAttribute(), чтобы получить значение атрибута [aria-expanded] на элементе this.trigger.

Если это значение равно true, то содержимое развёрнуто и его следует скрыть. Если нет, то оно скрыто и его следует показать.

Мы установим или уберём атрибут [hidden] у this.content, соответственно, и обновим значение атрибута [aria-expanded], чтобы оно соответствовало текущему состоянию.

/**
* Обработка событий в веб-компоненте
* @param {Event} event объект Event
*/

handleEvent (event) {

// Не позволяем кнопке запускать другие действия
event.preventDefault();

// Если содержимое развёрнуто, скроем его
// В противном случае покажем его
if (this.trigger.getAttribute('aria-expanded') === 'true') {
this.trigger.setAttribute('aria-expanded', false);
this.content.setAttribute('hidden', '');
} else {
this.trigger.setAttribute('aria-expanded', true);
this.content.removeAttribute('hidden');
}

}

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

See the Pen

Стилизация

Приятным моментом в использовании соответствующих ARIA-атрибутов (например, [aria-expanded]) для интерактивных элементов то, что их можно использовать для стилизации элементов в зависимости от текущего состояния элемента.

Например, вы можете использовать атрибут [aria-expanded], чтобы показывать значки на кнопке в зависимости от того, видно содержимое или нет.

show-hide [aria-expanded="true"] {
/* Стили видимого контента */
}

show-hide [aria-expanded="false"] {
/* Стили скрытого контента */
}

Дополнительные материалы

Предыдущая Статья

Как скрыть полосу прокрутки (скроллбар) с помощью CSS

Следующая Статья

Методы оптимизации SQL-запросов в высоконагруженных приложениях