Шесть встроенных функций HTML, заменяющих распространённые библиотеки интерфейса на JavaScript
Узнайте, как popover, эксклюзивные детали, диалоговые окна, Declarative Shadow DOM, fetchpriority и datalist заменяют пользовательский JavaScript, а также какие ограничения у каждого из них всё ещё существуют.
Источники: HTML Features in 2026 That Most Devs Don’t Know Exist
В течение многих лет HTML обеспечивал структуру, а JavaScript — всю поведенческую логику: модальные окна требовали библиотек React, подсказки — Popper.js или Tippy, а компоненты, генерируемые на сервере, — пользовательских скриптов. Тем временем стандарт HTML Living Standard от WHATWG и движки браузеров добавили встроенные элементы, позволяющие решить многие из этих задач с помощью нескольких атрибутов, что сокращает объем кода и размер бандла, а также обеспечивает надлежащую доступность по умолчанию. В этом руководстве рассматриваются шесть таких элементов, маркировка, необходимая для их использования, а также случаи, когда всё ещё требуются CSS или JavaScript. Перед использованием альтернативных решений убедитесь в поддержке выбранными браузерами.
1. Подсказки с атрибутом popover
Всплывающие подсказки и меню традиционно требовали борьбы с контекстами наложения z-index и ручной настройки обработчиков нажатий вне элемента. Благодаря API Popover любой элемент с атрибутом popover при открытии перемещается на верхний уровень, выше всего обычного контента, независимо от значения z-index. Кнопка, у которой в атрибуте popovertarget указано значение id элемента, отвечает за его открытие и закрытие:
<!-- The Trigger -->
<button popovertarget="my-menu">Toggle Menu</button>
<!-- The Popover Content -->
<div id="my-menu" popover>
<p>I am natively managed! Press ESC or click outside to close me.</p>
</div>
Без использования JavaScript получаем:
- Декларативное управление: кнопка открывает и закрывает всплывающее окно.
- Простое закрытие: достаточно нажать вне элемента или клавишу Escape, чтобы закрыть его. Это касается стандартного режима
popover="auto"; приpopover="manual"закрытие остается на ваше усмотрение. - Отсутствие проблем с наложением: открытое всплывающее окно отображается на верхнем уровне, выше всего остального.
Два ограничения: API контролирует видимость и стекирование элементов, но не их размещение, поэтому для привязки меню к триггеру всё равно требуется CSS (классическое позиционирование или более новый, но менее широко поддерживаемый способ позиционирования с использованием анкоров). Кроме того, поп-овер не является модальным и не блокирует фокус, поэтому для подтверждения действий следует использовать тег dialog.
2. Исключительные аккордеоны с описанием и именем
Теги details и summary уже давно позволяют создавать складываемые разделы без использования JavaScript. Нововведением является возможность объединения элементов details с одинаковым значением атрибута name в группу, в которой может быть открыт только один раздел, как в классических аккордеонах.
<details name="faq">
<summary>What is modern HTML?</summary>
<p>A living standard evolving directly inside browser engines.</p>
</details>
<details name="faq">
<summary>Do I still need JavaScript?</summary>
<p>Much less of it than you used to!</p>
</details>
<details name="faq">
<summary>Is this supported natively?</summary>
<p>Yes, across all major modern browser engines.</p>
</details>
Открытие одного элемента с атрибутом name="faq" закрывает все другие открытые элементы с таким же именем, причём это происходит без использования слушателей событий, хуков состояния или изменения классов. Браузеры, не поддерживающие атрибут name, просто позволяют иметь несколько открытых элементов, что является гибким решением в качестве альтернативы.
3. Доступные модальные окна с элементом dialog и ::backdrop
Собственные надстроки в виде div часто не проходят аудиты доступности, поскольку корректное модальное окно должно удерживать фокус внутри себя, блокировать содержимое страницы позади него и восстанавливать фокус на триггере после закрытия. Элемент dialog решает эту проблему. Ниже приведен форма с параметром method="dialog", которая закрывает диалоговое окно при нажатии любой из кнопок, причем значение нажатой кнопки становится значением returnValue диалогового окна:
<dialog id="user-modal">
<h2>Confirm Action</h2>
<p>Are you sure you want to delete this repository?</p>
<!-- A form inside a dialog with method="dialog" closes it automatically -->
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Delete</button>
</form>
</dialog>
<button onclick="document.getElementById('user-modal').showModal()">
Open Modal
</button>
Затемненная надстрока — это псевдоэлемент ::backdrop, стилизуемый с помощью обычного CSS:
/* Styling the native browser backdrop */
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
}
showModal() размещает диалоговое окно на верхнем уровне, делает остальную часть страницы неактивной, сохраняет фокус внутри окна и позволяет закрыть его с помощью клавиши Escape; при закрытии современные браузеры возвращают фокус на элемент, который был активен ранее. show(), напротив, открывает окно в режиме, не являющемся модальным, без этих мер защиты. В React следует вызывать showModal() через ref вместо inline-атрибута onclick.
4. Веб-компоненты, генерируемые на сервере, с декларативным Shadow DOM
Ранее для работы Shadow DOM требовался клиентский JavaScript (element.attachShadow({ mode: 'open' })), поэтому веб-компоненты, генерируемые на сервере или статически, не имели инкапсулированного маркиупа до запуска скриптов, что приводило к мимолетному отсутствию стилей и смещениям макета. Декларативный Shadow DOM решает эту проблему: template с атрибутом shadowrootmode внутри хост-элемента описывает структуру тени на языке HTML:
<user-card>
<template shadowrootmode="open">
<style>
/* Styles here stay encapsulated inside this component */
h3 { color: #3b82f6; font-family: sans-serif; }
</style>
<h3>Jane Doe</h3>
<p>Senior Frontend Engineer</p>
</template>
</user-card>
Парсер преобразует шаблон в теневой корень для элемента user-card перед отрисовкой, благодаря чему стили и маркировка с ограниченной областью применения появляются сразу при первой отрисовке. Однако это происходит только во время парсинга HTML. Маркировка, вставленная позже с помощью innerHTML, не создает теневого корня; для динамической вставки требуются такие API, как setHTMLUnsafe(), поэтому необходимо проверить текущую поддержку.
5. Определение приоритета критически важных ресурсов с помощью fetchpriority
Атрибуты loading="lazy" и async/defer не позволяют указать, какие ресурсы должны загружаться в первую очередь. Если есть несколько изображений выше линии прокрутки, изображение-герой, находящееся после момента Largest Contentful Paint (LCP), должно иметь приоритет перед значками и иконками. Атрибут fetchpriority предоставляет сетевому планировщику такую информацию:
<!-- Critical LCP Image: Load this immediately! -->
<img src="hero-banner.webp" fetchpriority="high" alt="Product Hero">
<!-- Lower priority images -->
<img src="avatar.webp" fetchpriority="low" alt="User avatar">
<!-- Critical JavaScript bundle -->
<script src="app.js" fetchpriority="high"></script>
Это может улучшить показатели Core Web Vitals без изменения логики приложения. Это рекомендация, а не команда, и отметка многих ресурсов как high сводит эффект к нулю. Также никогда не используйте технику lazy-load для изображения LCP.
6. Встроенные предложения с datalist
Во многих проектах добавляют тяжелый компонент автодополнения лишь для предложения значений при возможности ввода свободного текста. datalist делает это встроенно: свяжите его с помощью атрибута list элемента ввода, и его опции станут предложениями.
<label for="framework">Choose or type a framework:</label>
<input list="frameworks" id="framework" name="framework" placeholder="e.g. Next.js">
<datalist id="frameworks">
<option value="Next.js"></option>
<option value="SvelteKit"></option>
<option value="Nuxt.js"></option>
<option value="Astro"></option>
<option value="Remix"></option>
</datalist>
Браузер отрисовывает и фильтрует список, обрабатывает навигацию с клавиатуры и следует стилистике платформы. Компромисс заключается в уровне контроля: стилизация минимальна, поведение различается в браузерах и на мобильных устройствах, а сложные опции невозможны. Идеально подходит для простых полей «выбрать или ввести»; для брендированных комбоксов всё равно требуется отдельный компонент.
Встроенная функция против старого подхода
- Подсказки и меню: использование элемента
popoverвместо библиотек для позиционирования, а также обработка кликов вне элемента и нажатия клавиши Escape (само позиционирование по-прежнему реализуется с помощью CSS). - Аккордионы: элементы
detailsс общим атрибутомnameвместо логики управления состоянием и переключения в JavaScript. - Модальные окна: использование элемента
dialogс методамиshowModal()и::backdropвместо пользовательского оверлея с ручным управлением фокусом. - Компоненты, генерируемые на сервере: использование параметра
template shadowrootmodeвместо вызовов методаattachShadow(), доступного только на стороне клиента. - Приоритеты загрузки: использование атрибута
fetchpriorityвместо ручных методов предзагрузки или управления порядком загрузки скриптов.
datalist вместо пользовательского компонента автодополнения.Чтобы узнать больше о функциях платформы, способных заменить сторонние зависимости, ознакомьтесь с нашим обзором нативных API браузера, заменяющих популярные пакеты npm.
Основные выводы
- Браузеры теперь предоставляют функционал, ранее требовавший использования библиотек, причем часто с встроенной обработкой фокуса и возможностью закрытия по нажатию клавиши Escape.
- Нативный подход не означает полноты: для отображения поп-апов требуется CSS,
datalistпозволяет лишь ограниченную настройку стилей, а декларативный Shadow DOM работает только во время парсинга. - Прежде чем добавлять зависимость для интерфейса, проверьте, не покрывает ли какой-либо элемент или атрибут реальные потребности ваших поддерживаемых браузеров.