Пользовательские элементы обычно наследуются от интерфейса
HTMLElement, который представляет общий HTML-элемент. Есть возможность наследования от других, более конкретных элементов, используя несколько многословный синтаксис и атрибут is.<script>
class ToggleButton extends HTMLButtonElement {
connectedCallback() {
// Логика
}
}
customElements.define(
'toggle-button',
ToggleButton,
{
extends: 'button'
}
);
</script>
<button
type="button"
is="toggle-button"
data-target="content"
>
Подробнее
</button>
<div id="content" hidden>
<!-- Скрытый контент -->
</div>
Идея в том, чтобы расширять существующие элементы новым поведением, сохраняя семантику, доступность и встроенное поведение. В случае с кнопкой это фокусировка, активация с помощью клавиатуры, встроенная роль, отключение и так далее.
Этот API называется «Customized built-in elements» и давно поддерживается во всех браузерах, кроме Safari. Браузерные инженеры из Apple принципиально отказались реализовывать этот API, сославшись на целый ряд причин.
Если углубиться в детали, то причины вполне адекватные. Из-за отказа Apple от внедрения, Customized built-in elements практически не используются. Есть несколько альтернативных идей, которые учитывают обратную связь от Apple.
Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.
Одна из таких идей, Custom Attributes, обсуждалась на недавнем TPAC 2025. Есть несколько разных предложений, но основная суть в том, чтобы добавить API для создания пользовательских атрибутов по аналогии с пользовательскими элементами.
Атрибуты могут наследоваться от интерфейса
Attr, который уже существует и представляет узел атрибута в DOM-дереве. У пользовательских атрибутов будут методы жизненного цикла для произвольной логики. В итоге получатся миксины для расширения элементов.<!--
Это гипотетический код нового API
Он не работает ни в одном из браузеров
-->
<script>
class ToggleElementAttr extends Attr {
// Встроенный тип данных
static dataType = AttributeType.IDREF;
// Опциональное значение по умолчанию
static defaultValue = null;
// Элемент, у которого установлен атрибут
ownerElement;
// Текущее значение атрибута
value;
// Атрибут подключен к элементу
connectedCallback() {
// Логика
}
// Атрибут удалён
disconnectedCallback() {}
// Атрибут изменился
changedCallback(oldValue, newValue) {}
}
HTMLButtonElement.attributeRegistry.define(
'toggle-element',
ToggleElementAttr
);
</script>
<button
type="button"
toggle-element="content"
>
Подробнее
</button>
<div id="content" hidden>
<!-- Скрытый контент -->
</div>
Это предложение также решает проблему HTML-first библиотек, таких как HTMX, Alpine.js, UIKit и других, которые используют нестандартные атрибуты для декларативного добавления логики и анализируют их с помощью регулярных выражений или XPath.
Custom Attributes API позволит убрать лишний код, который отвечает за поиск и разбор атрибутов и их значений. Работа с произвольными атрибутами должна стать удобнее и проще. Также пользовательские атрибуты будут считаться валидными.
<script>
// Где-то в коде htmx.js может быть так:
class HxPost extends Attr {
// ...
}
class HxSwap extends Attr {
// ...
}
const buttonAttrMap = {
'hx-post': HxPost,
'hx-swap': HxSwap,
// ...
}
for (const name in buttonAttrMap) {
HTMLButtonElement.attributeRegistry.define(
name,
buttonAttrMap[name]
);
}
</script>
<button hx-post="/clicked" hx-swap="outerHTML">
Нажми меня
</button>
#html #js #web_api