Улучшение веб-компонента

Источник: «Progressively enhancing a Web Component»
Сегодня рассмотрим одну из моих любимых фич веб-компонентов: прогрессивное улучшение.

На этой неделе мы рассмотрели, как создать первый веб-компонент и как добавить опции в веб-компонент.

Несколько разных подходов

Существует два подхода к прогрессивному улучшению веб-компонента (возможно, их больше, но я чаще всего использую именно эти два)…

  1. Если контент работает только с JavaScript, скройте его и показывайте только после создания экземпляра веб-компонента.
  2. Если контент работает без JavaScript, покажите HTML по умолчанию, а затем добавьте дополнительные интерактивные возможности на основе HTML и JavaScript после создания экземпляра веб-компонента.

Мы рассмотрим оба подхода, а также то, когда и почему стоит предпочесть один из них другому.

Подход 1. Скрыть контент

Для этого подхода давайте рассмотрим пример, используемый нами всю неделю, — компонент <wc-count>, создающий кнопку-счётчик.

<wc-count>
<button>Clicked 0 Times</button>
</wc-count>

Кнопка абсолютно ничего не делает без JavaScript. Поэтому, вероятно, имеет смысл скрыть её, пока веб-компонент не будет готов.

Есть несколько способов сделать это…

Использование атрибута [hidden]

Один из способов сделать это — добавить к элементу атрибут [hidden].

<wc-count hidden>
<button>Clicked 0 Times</button>
</wc-count>

Затем в методе constructor() при создании компонента можно удалить атрибут.

/**
* Конструктор класса
*/

constructor () {

// Всегда вызывайте super первым в конструкторе
super();

// ...

// Показываем элемент
this.removeAttribute('hidden');

}

Использование CSS

Псевдокласс :defined предоставляет ещё один способ обнаружить, когда пользовательский элемент был определён с помощью только CSS, а не JavaScript.

Можно комбинировать его с псевдоклассом :not(), чтобы скрыть элемент, пока он не будет определён.

/* Скроем элемент <wc-count> до тех пор, пока он не будет определён */
wc-count:not(:defined) {
display: none;
}

С резервным сообщением

Неплохо было бы загрузить резервное сообщение во время загрузки контента.

В этом примере обернём сообщение в элемент <wc-count-loading>, и добавим атрибут [hidden] к элементу <button>.

<wc-count>
<button hidden>Clicked 0 Times</button>
<wc-count-loading>Loading...</wc-count-loading>
</wc-count>

Затем в методе constructor() удалим атрибут [hidden] из this.button. Также используем метод Element.querySelector(), чтобы получить элемент <wc-count-loading>, и метод Element.remove(), чтобы удалить его, когда контент будет готово.

/**
* Конструктор класса
*/

constructor () {

// Всегда вызывайте super первым в конструкторе
super();

// Свойства экземпляра
this.button = this.querySelector('button');
// ...

// Показываем элемент
this.button.removeAttribute('hidden');
this.querySelector('wc-count-loading')?.remove();

}

Подход 2. Слои в HTML и интерактивность

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

Например, представим, что вы создаёте группу аккордеонов с различными секциями раскрытия/свёртывания.

Можно начать с HTML, включающего заголовки и контент, например, так...

<accordion-group>
<h2>Why is Lil' Wayne hip-hop top 5 list?</h2>
<div>
<p>...</p>
</div>

<h2>How many spells could Merlin cast in a day?</h2>
<div>
<p>...</p>
</div>
</accordion-group>

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

/**
* Создание экземпляра веб-компонента
*/

constructor () {

// Получаем все заголовки аккордеона
let headings = this.querySelectorAll('h2');

// Обновляем контент
for (let heading of headings) {

// Получаем совпадающий контент
let content = heading.nextElementSibling;
if (!content) continue;

// Создаём кнопку и копируем в неё содержимое заголовка
let btn = document.createElement('button');
btn.innerHTML = heading.innerHTML;

// Стираем содержимое заголовка и заменяем его кнопкой
heading.innerHTML = '';
heading.append(btn);

// Скрываем контент
content.setAttribute('hidden', '');

// Добавляем ARIA
btn.setAttribute('aria-expanded', false);

}

}

В итоге получаем HTML, выглядящий примерно так...

<accordion-group>
<h2><button>Why is Lil' Wayne hip-hop top 5 list?</button></h2>
<div hidden>
<p>...</p>
</div>

<h2><button>How many spells could Merlin cast in a day?</button></h2>
<div hidden>
<p>...</p>
</div>
</accordion-group>

Какой подход использовать

Я предпочитаю начинать с базового HTML и добавлять интерактивность, когда это возможно.

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

Все статьи серии о Веб-Компонентах/Web Component

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

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

Добавление опций в веб-компонент

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

Что может сломать aspect-ratio в CSS