Перейти к содержанию

Всплывающий блок с Popover API

Не всякое дополнительное пояснение должно блокировать страницу. Для небольшой подсказки полезнее позволить человеку продолжать работу с каталогом. Popover API предоставляет всплывающее состояние и размещение в top layer, сохраняя немодальный характер взаимодействия. Важно не путать этот механизм с ролью содержимого: подсказка остаётся пояснением, а список действий потребовал бы другой структуры и управления.

Продолжим страницу с диалогом из предыдущего урока. Добавим независимый блок помощи, который изначально является обычным видимым aside. Только при поддержке API превратим его во всплывающий. Результат — кнопка открывает и закрывает небольшую подсказку; без нового механизма сохраняются ссылка-якорь и текст в потоке страницы.

Сначала обычный блок и ссылка

В index.html вставьте следующий полный блок перед существующим details.lab-details, после секции помощи и диалога предыдущего урока. Не меняйте идентификаторы диалога. Новая подсказка получает собственное имя, элементы управления и цель якоря.

<p class="lab-help-controls">
<a id="help-fallback" href="#catalog-help">Подсказка по работе с каталогом</a>
<button id="help-toggle" type="button" popovertarget="catalog-help" hidden>Подсказка по каталогу</button>
</p>
<aside id="catalog-help" class="lab-help" aria-labelledby="help-title">
<h2 id="help-title">Обычные ссылки ведут к урокам</h2>
<p>Откройте оглавление курса, чтобы выбрать первую страницу. Учебная заявка только показывает параметры адреса.</p>
<button id="help-close" type="button" popovertarget="catalog-help" popovertargetaction="hide" hidden>Закрыть подсказку</button>
</aside>

В исходном HTML у aside нет атрибута popover. Поэтому без JavaScript он остаётся обычным видимым содержимым страницы. Ссылка help-fallback переводит к существующему блоку. Кнопки включения и закрытия скрыты до установки нового механизма; они не должны обещать действие в браузере, где соответствующее поведение ещё не доступно.

Если бы мы сразу записали popover="auto", поддерживающий API браузер скрыл бы содержимое до открытия. Без дополнительной стратегии это нарушило бы выбранную основу при отключённом скрипте. Здесь намеренно выбран другой порядок: сначала доступный текст, затем ограниченное улучшение. Возможности декларативного HTML сами по себе полезны, но должны соответствовать конкретному запасному состоянию проекта.

Проверяем функцию и меняем один режим

Оставьте в ui.js полный код диалога из предыдущего урока. После него один раз добавьте следующий блок. Не создавайте второе подключение скрипта и не заменяйте прежние имена opener и dialog: новая часть использует отдельные имена и не должна ломать уже установленное поведение.

const help = document.querySelector('#catalog-help');
const helpToggle = document.querySelector('#help-toggle');
const helpClose = document.querySelector('#help-close');
const helpFallback = document.querySelector('#help-fallback');
if (help && helpToggle && helpClose && helpFallback &&
    typeof help.showPopover === 'function') {
  help.popover = 'auto';
  helpToggle.hidden = false;
  helpClose.hidden = false;
  helpFallback.hidden = true;
}

Проверяется наличие четырёх ожидаемых узлов и функции showPopover. Если условие выполнено, свойство popover получает режим auto; затем открываются две управляющие кнопки и скрывается обычная ссылка. Если функция отсутствует, изменения не выполняются. Так обе ветки имеют понятный интерфейс: обычный блок со ссылкой либо поддерживаемый всплывающий блок с кнопкой.

Сам метод здесь служит проверкой, а не вызывается для каждого нажатия. Открытие выполняет нативная связь popovertarget на кнопке. Значение указывает на идентификатор подсказки. У второй кнопки popovertargetaction="hide" задаёт явное закрытие. Это позволяет увидеть декларативное управление после небольшой начальной проверки, не дублируя действие отдельным JavaScript-обработчиком.

Ожидается, что нажатие основной кнопки переключает подсказку, а внутренняя кнопка закрывает её. Тип button не даёт случайно отправить учебную форму при изменении места контролов. Они вообще находятся вне той формы, но явный тип сохраняет понятное назначение. Дополнительное пояснение не принимает пользовательские данные и не выполняет сетевой запрос.

Auto не означает модальность

В режиме auto всплывающий элемент поддерживает обычное закрытие при взаимодействии снаружи и платформенное действие отмены, например Escape. Открытие другого автоматического popover также влияет на порядок таких элементов, с отдельными правилами для вложенных состояний. В нашем проекте автоматическая подсказка одна, поэтому не нужно вводить искусственный менеджер нескольких вложенных окон.

Popover не делает остальную страницу инертной так, как модальный dialog.showModal. После открытия пользователь должен иметь возможность взаимодействовать с другими частями каталога. Поэтому мы не устанавливаем aria-modal, не блокируем Tab самодельным циклом и не переносим всем фоновым ссылкам tabindex. Применение этих приёмов превратило бы выбранный механизм в другое, несогласованное поведение.

API определяет отображение и управление состоянием, а не автоматически превращает содержимое в меню, tooltip или диалог. Наш aside содержит заголовок, объяснение и кнопку закрытия. Если будущий компонент получит команды навигационного меню, его семантику и клавиатурный маршрут потребуется спроектировать отдельно. Наличие слова popover не заменяет такое решение.

Механизм атрибута и кнопок объясняет MDN об использовании Popover API, а модель автоматического состояния описана в HTML Standard. Для конкретной поддержки проверяйте используемые части API в первичном справочнике. Мы не назначаем одну дату универсальным доказательством работоспособности всех связанных новых свойств.

Положение без новой системы якорей

Добавьте следующий полный блок в конец styles.css. Он оформляет как обычное, так и всплывающее состояние. Новые свойства якорного позиционирования здесь не нужны: окно получает простое ограниченное размещение по центру.

@layer components {
.layout-lab .lab-help-controls, .layout-lab .lab-help { margin-block-start: 1.5rem; }
.layout-lab .lab-help { padding: 1rem; border: 1px solid var(--lab-line); background: var(--lab-surface); color: var(--lab-ink); }
.layout-lab .lab-help > * + * { margin-block-start: .75rem; }
.layout-lab .lab-help[popover] { position: fixed; inset: 0; margin: auto; inline-size: min(90%, 30rem); max-block-size: 80vh; overflow: auto; }
.layout-lab :is(.lab-help-controls, .lab-help) button { font: inherit; padding: .5rem .75rem; cursor: pointer; }
}

Обычный блок имеет отступ сверху и рамку в потоке. После добавления атрибута правило [popover] задаёт фиксированное положение, нулевые стороны и автоматические внешние отступы. Ограниченная ширина и высота позволяют сохранять место для текста. Псевдосостояние открытия предоставляет браузер; не задаём компоненту безусловный display: block, который мог бы вмешаться в скрытое состояние нативного механизма.

Размещение в top layer позволяет не соревноваться с sticky-панелью каталога одним огромным z-index. При этом содержание по-прежнему связано со своей кнопкой и названием. Мы не вычисляем координату кнопки в JavaScript и не обещаем прикрепление подсказки непосредственно к её нижней границе. Если потребуется такое положение, оно станет отдельным результатом с собственными условиями поддержки.

Кнопка открытия не получает autofocus внутри подсказки по нашему выбору. Начальный маршрут фокуса и нативная связь должны быть проверены в конкретном окружении. Не предполагаем, что немодальное открытие обязательно переводит фокус тем же способом, что диалог предыдущего урока. Внутренняя кнопка остаётся доступным явным способом закрытия без требования угадывать действие вне окна.

Две независимые ветки проверки

Для последующего ручного опыта отключите весь ui.js. Ожидается обычная ссылка, видимый aside, скрытые дополнительные кнопки и обычная секция пояснения перед ним. Затем восстановите скрипт и проверьте поддерживаемое открытие, повторное нажатие, явное закрытие, Escape и нажатие снаружи. Такие наблюдения при подготовке урока не выполнялись; описан ожидаемый маршрут по сохранённому коду.

Диалог и подсказка устанавливаются отдельными условиями. Отсутствие одного API не должно автоматически лишить страницу второго. В нашем файле нет общего раннего выхода, который прекращал бы всю настройку после неподдерживаемого диалога. Это маленькая, но существенная часть постепенного улучшения: каждая новая возможность отвечает за свою основу и свой контроль.

Основная задача чтения курсов не зависит от обоих всплывающих механизмов. Оглавления находятся в обычных ссылках, а форма честно обозначена учебной. Теперь соберём связанные CSS-правила более компактно через нативную вложенность, сохраняя понимание их специфичности и поддерживаемого запасного варианта.

Полное состояние этого шага находится в layout-lab/lesson-30 внутри исходников продолжения. Каждый снимок открывается отдельно; подключать CSS разных уроков одновременно не нужно.

Оглавление курса.