Всплывающая справка без управления слоями
Диалог предыдущего этапа временно отделял свою задачу от остального каталога. Теперь добавим справку, которую можно читать одновременно со страницей. Для неё подходит popover: элемент становится всплывающим, но не превращается в модальное окно. Посетитель сохраняет доступ к списку материалов. Цель урока — показать и убрать дополнительную панель средствами платформы, не создавая собственный менеджер слоёв.
Начальное содержимое справки находится в обычном aside. Это важное решение для постепенного улучшения. Атрибут popover в поддерживающем браузере скрывает элемент, пока он не открыт. Если сразу поместить его в HTML, а наш сценарий не загрузится, управляющая кнопка может остаться скрытой вместе с единственным способом открыть текст. Поэтому начальная версия документа показывает справку в потоке страницы.
Справка, которая не блокирует страницу
Сценарий проверяет наличие popover в прототипе HTMLElement. При положительном результате он назначает панели значение auto и показывает кнопку. Эта кнопка уже связана с панелью через popovertarget. Её действие реализует браузер; отдельный click-обработчик для переключения состояния не нужен. Изменение конструкции следует делать целиком: включение всплывающего режима и появление управляющего элемента составляют одно улучшение.
Режим auto допускает обычное закрытие, включая действие за пределами панели и Escape. Поведение группы всплывающих элементов имеет свои правила. Если позднее появится несколько связанных панелей, не предполагайте, что все они будут независимо открыты одновременно. manual предоставляет другой договор, но потребует явного управления выходом. Нельзя выбирать его только ради того, чтобы справка дольше оставалась на экране.
Popover попадает в верхний слой и поэтому не обрезается обычным overflow предка так, как произвольный абсолютный блок. Это решает часть задач рисования, но не решает задачу размещения рядом с кнопкой. Пока панель использует базовое положение браузера. Привязку через CSS добавим отдельным этапом: тогда будет видно, какой механизм отвечает за состояние, а какой за геометрию.
Не назначаем панели роль меню. Она содержит заголовок и поясняющий текст, а не набор команд с особой клавиатурной моделью. Нативное всплывающее состояние не отменяет необходимости подобрать семантику содержимого. Если внутри появится форма, ссылки или переключатели, каждое действие должно оставаться доступным обычным способом. popover сам по себе не делает произвольный набор div полноценным меню.
Ожидаемое состояние после улучшения: справка исчезает из потока, появляется кнопка и открывает немодальную панель. При отсутствующей поддержке тот же текст остаётся видимым ниже введения. Размер и положение карточек могут измениться, однако пользователь получает содержание в обеих ветках. Это полезный критерий совместимости: совпадение пикселей не является обязательным, сохранение основной задачи является.
В будущем просмотре сравните поведение Tab и Escape с модальным диалогом. Фоновая ссылка остаётся частью маршрута, а переход к другому материалу не требует сначала подтверждать справку. Если текст содержит критическое предупреждение, такой способ подачи может быть недостаточным. Важное условие открытия урока лучше оставить в основном документе, а всплывающую панель использовать для дополнительного пояснения.
Полный файл index.html текущего этапа:
<!doctype html>
<html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Учебный каталог</title><link rel="stylesheet" href="styles.css"><script type="module" src="app.js"></script></head>
<body><a href="#main">Перейти к материалам</a><header><a href="index.html">Учебный каталог</a></header><main id="main"><h1>Учебные материалы</h1><p>Каталог «Полка»: статьи о контенте и интерфейсах.</p><button type="button" id="open-lesson" hidden>О проекте урока</button>
<dialog id="lesson-dialog" aria-labelledby="dialog-title"><h2 id="dialog-title">Учебная библиотека</h2><p>Три урока с постоянными ссылками. Карточка дополняет каталог.</p><form method="dialog"><button value="close" autofocus>Закрыть</button></form></dialog><button type="button" id="help-button" popovertarget="help" hidden>Как читать каталог</button>
<aside id="help"><h2>Чтение каталога</h2><p>Откройте название урока. Справка не закрывает доступ к другим ссылкам.</p></aside><ul class="cards"><li class="card" data-topic="content" data-minutes="20"><h2><a href="article.html#markdown">Markdown и структура урока</a></h2><p class="card-description">Разделяем содержание и оформление библиотеки.</p><p class="card-meta">Контент · 20 минут</p></li>
<li class="card" data-topic="interface" data-minutes="35"><h2><a href="article.html#navigation">Навигация учебного каталога</a></h2><p class="card-description">Сохраняем обычные ссылки и последовательность чтения.</p><p class="card-meta">Интерфейс · 35 минут</p></li>
<li class="card" data-topic="content" data-minutes="25"><h2><a href="article.html#urls">Постоянные адреса материалов</a></h2><p class="card-description">Меняем каталог, сохраняя адрес каждой статьи.</p><p class="card-meta">Контент · 25 минут</p></li></ul><p class="notice">Каждый урок доступен по обычной ссылке. Фильтры и эффекты дополняют чтение.</p></main><footer>Полка · учебный проект</footer></body></html>
Полный файл app.js текущего этапа:
document.documentElement.dataset.enhanced = 'true';
const lessonDialog = document.querySelector('#lesson-dialog');
const openLesson = document.querySelector('#open-lesson');
if (typeof lessonDialog.showModal === 'function') {
openLesson.hidden = false;
openLesson.addEventListener('click', () => {
if (!lessonDialog.open) lessonDialog.showModal();
});
lessonDialog.addEventListener('close', () => openLesson.focus());
}
if ('popover' in HTMLElement.prototype) {
document.querySelector('#help').setAttribute('popover', 'auto');
document.querySelector('#help-button').hidden = false;
}
Полный файл styles.css текущего этапа:
* { box-sizing: border-box; }
:root { color-scheme: light; --accent: #6d28d9; --paper: #fff; --ink: #20202a; }
body { margin: 0; color: var(--ink); background: #f7f6fb; font: 1rem/1.6 system-ui, sans-serif; }
header, main, footer { max-width: 72rem; margin-inline: auto; padding: 1.5rem; }
a { color: #5923b5; text-underline-offset: .18em; }
button { font: inherit; padding: .5rem .9rem; cursor: pointer; }
:focus-visible { outline: 3px solid #17693d; outline-offset: 4px; }
[hidden] { display: none !important; }
.cards { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); list-style: none; padding: 0; }
.card, aside { padding: 1.25rem; border: 1px solid #ddd8e8; border-radius: .7rem; background: white; }
.card h2 { margin-block-start: 0; }
.card-meta { color: #51495f; }
.notice { border-inline-start: .25rem solid var(--accent); padding-inline-start: 1rem; }
dialog { width: min(36rem, calc(100vw - 2rem)); border: 1px solid #b9afca; padding: 1.5rem; border-radius: .8rem; }
dialog::backdrop { background: rgb(20 15 30 / .5); }
Разбор изменения каталога
Кнопка открытия является понятным ориентиром, поэтому справку не показываем только по наведению. Наведение зависит от мыши и не выражает устойчивое намерение читать абзац. Клавиатура и сенсорный экран должны иметь тот же осмысленный путь. У всплывающей панели могут быть и другие способы вызова, но они требуют отдельного договора о доступном действии и закрытии.
Когда справка открывается, не нужно автоматически объявлять весь её текст через громкое live region. Пользователь выбрал кнопку и может перейти к содержимому. Избыточное объявление длинного абзаца способно мешать чтению. Если внутри панели меняются данные в ответ на запрос, сообщение о результате следует проектировать отдельно, сохранив спокойный порядок доступа к содержимому.
Для самостоятельного варианта добавьте в справку ссылку на раздел навигации в article.html. Она должна оставаться обычной ссылкой и после включения popover. Затем временно исключите строку назначения атрибута. В этом состоянии справка будет частью документа, а ссылка сохранит назначение. Такая проверка подтверждает именно смысловое соответствие двух веток, не требуя одинакового положения каждого элемента.
Разберём случай, когда на странице одновременно есть диалог и popover. Они решают разные задачи и могут иметь разные правила выхода. Не следует закрывать обе панели каждым нажатием Escape собственным общим обработчиком. Браузер уже знает состояние нативных элементов; лишний перехват может скрыть больше, чем ожидал человек. Если продукт требует совместной логики, сначала определите порядок задач и конкретные события.
Управляющая кнопка и текст справки не обязаны находиться рядом в DOM, но порядок чтения должен оставаться понятным. В нашем небольшом документе они расположены вместе. Когда блок переносится в общий футер ради удобства шаблона, стоит пересмотреть его место для клавиатуры и вспомогательных технологий. Верхний слой решает рисование, однако не стирает структуру документа.
Не путайте раскрытую справку с выбранной темой. Popover описывает чтение каталога, а фильтр позже изменит состав карточек. Если закрытие справки неожиданно сбрасывает тему, два независимых состояния оказались связаны без основания. Дополнительные панели должны дополнять выбранную задачу, а не молча менять исходные данные пользователя.
Для самостоятельного разбора составьте две ветки: текст в потоке и текст во всплывающем состоянии. В обеих должен существовать один и тот же смысл абзаца. Меняться могут положение и способ выхода, но не требование прочитать важные условия. Если при улучшении часть условия исчезает или переносится в недоступный элемент, это ошибка дизайна ветки, а не нормальная разница поддержки.
Условия для самостоятельного наблюдения
Это план будущего разбора, а не журнал выполненных проверок. Рассмотрите одинаковые исходные данные и сопоставьте действие с ожидаемым состоянием.
| Условие | Ожидаемое состояние и его граница |
|---|---|
| Поддержка отсутствует | Aside остаётся обычным содержимым и не требует открытия отдельной кнопкой. |
| Сценарий не загрузился | В HTML не задан скрывающий режим popover; текст справки доступен в потоке. |
| Кнопка активирована | Связь popovertarget указывает на единственную панель с нужным id. |
| Действие вне панели | Режим auto допускает закрытие без пользовательского менеджера обработчиков. |
| Переход к статье | Ссылка каталога сохраняет действие, пока справка открыта. |
| В панели появился пункт команды | Требуется пересмотреть семантику содержимого, а не автоматически назначать роль menu. |
Оглавление серии, курсы ProfessorWeb и полный архив исходников. Для этого урока выберите lesson-03.
Первичные источники: Popover attribute.
Редакционный статус: локальный черновик. Установка, учебные программы, браузерные сценарии, тесты и публикация не выполнялись. Описанные эффекты ожидаемые.