Главная / Статьи

Шесть встроенных функций HTML, заменяющих распространённые библиотеки интерфейса на JavaScript

Узнайте, как popover, эксклюзивные детали, диалоговые окна, Declarative Shadow DOM, fetchpriority и datalist заменяют пользовательский JavaScript, а также какие ограничения у каждого из них всё ещё существуют.

Издатель: REACTAPP.TOP6 мин чтения1277 слов

Источники: 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 работает только во время парсинга.
    • Прежде чем добавлять зависимость для интерфейса, проверьте, не покрывает ли какой-либо элемент или атрибут реальные потребности ваших поддерживаемых браузеров.