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

Диалог увеличения изображения

На учебной странице уже есть схема XML и текст её структуры. Увеличение может помочь рассмотреть детали, но оно не должно превращаться в ловушку управления. Читателю нужно открыть объект, понять новое место, закрыть его и вернуться к тому же действию в статье.

В этом уроке вы добавите необязательный диалог к a11y-lab после двенадцатой главы. Это самостоятельный учебный прототип, а не существующая функция ProfessorWeb. Используем настоящий элемент dialog и сохранённую схему images/employees-tree.svg; собственный псевдодиалог из div не понадобится.

От ссылки к модальному просмотру

У схемы остаётся содержательная подпись: Employees содержит два Employee со значениями Alex и Elena. Диалог не заменяет эту информацию и не делает изображение обязательным способом получения результата. Обычная ссылка на SVG пригодится, если дополнительный скрипт недоступен или человек предпочитает отдельный просмотр.

Модальный просмотр отличается от раскрытия меню девятого урока. Пока меню открыто, читатель может продолжать движение к статье. При модальном диалоге взаимодействие с остальной страницей временно ограничивается. Поэтому одной записи aria-expanded недостаточно: меняются контекст и модель фокуса.

Название диалога должно сообщать, что открыто. «Просмотр» слишком неопределённо для страницы с несколькими изображениями. В нашем примере используем «Увеличенная схема Employees». Оно связано с существующим заголовком через aria-labelledby. Видимые слова и программное имя не расходятся.

Поведение модального окна описано в APG: вход внутрь, движение между его элементами, закрытие и осмысленный возврат. Это модель для будущего наблюдения, а не основание утверждать одинаковую озвучку во всех сочетаниях программ.

Разметка с существующим ресурсом

Внутри первого figure после подписи добавьте две альтернативные возможности:

<p><a href="images/employees-tree.svg">Открыть схему отдельно</a></p>
<button id="image-open" type="button" hidden>Увеличить схему Employees</button>

Перед закрывающим body добавьте один диалог. Этот фрагмент полный:

<dialog id="image-dialog" aria-labelledby="image-dialog-title">
  <h2 id="image-dialog-title">Увеличенная схема Employees</h2>
  <button id="image-dialog-close" type="button" autofocus>Закрыть просмотр</button>
  <figure>
    <img src="images/employees-tree.svg" width="640" height="260"
         alt="Дерево Employees; отношения и значения описаны в подписи.">
    <figcaption>Employees содержит два Employee. FirstName первого — Alex,
      второго — Elena. Descendants("Employee") выбирает оба элемента.</figcaption>
  </figure>
</dialog>

Идентификатор image-dialog соответствует договору серии. Остальные новые имена определены здесь один раз. У закрывающей кнопки есть обычный текст, а autofocus выбирает её при открытии. Мы не фокусируем диалог при загрузке страницы и не добавляем tabindex самому элементу dialog.

Встроенный элемент имеет собственную модель открытия и закрытия; особенности описаны в MDN. Не назначаем ему дополнительно role="dialog" ради имитации семантики. Открытие через showModal отличается от простого присутствия атрибута open: последнее само не создаёт модальную модель.

Открытие и возврат

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

const imageOpen = document.getElementById('image-open');
const imageDialog = document.getElementById('image-dialog');
const imageClose = document.getElementById('image-dialog-close');

if (typeof imageDialog.showModal === 'function') {
  imageOpen.hidden = false;
  imageOpen.addEventListener('click', () => {
    if (!imageDialog.open) imageDialog.showModal();
  });
  imageClose.addEventListener('click', () => imageDialog.close());
  imageDialog.addEventListener('close', () => {
    if (imageOpen.isConnected) imageOpen.focus();
  });
}

Кнопка появляется только после обнаружения необходимого метода. До этого обычная ссылка и текстовая структура остаются доступными. Такая модель не скрывает основную информацию в ожидании необязательного улучшения. Проверка метода не является полной оценкой поведения браузера, но делает исходную границу понятной.

showModal помещает диалог в специальный верхний слой и делает остальную страницу неактивной для этой модальной модели; механизм описан в MDN. Поэтому не нужно перекрывать весь сайт случайным огромным z-index или вручную назначать скрытие каждой области.

Обработчик close отвечает за возврат независимо от пути завершения. Кнопка вызывает close, а стандартное действие Escape может закрыть модальный диалог через браузер. Мы не перехватываем cancel с запретом закрытия. Не требуется отдельный глобальный обработчик Escape, который одновременно менял бы меню и диалог.

Если инициатор удалён, данная проверка не пытается сфокусировать отсутствующий элемент. В нашем статическом стенде он сохраняется. Для динамической страницы нужно определить другое место возврата по смыслу действия. Одна универсальная команда «фокусировать body» не восстановит читательский маршрут.

Размер и видимая граница

Добавьте в lesson.css следующий фрагмент:

#image-open[hidden] { display: none; }
#image-dialog { inline-size: min(60rem, calc(100% - 2rem)); max-inline-size: calc(100% - 2rem); max-block-size: calc(100% - 2rem); overflow: auto; padding: 1.25rem; border: 2px solid #56318c; }
#image-dialog img { display: block; inline-size: 100%; block-size: auto; }
#image-dialog::backdrop { background: rgb(0 0 0 / .55); }
#image-dialog-close { padding: .75rem 1rem; font: inherit; }

Не задаём display закрытому диалогу: его исходная невидимость должна сохраняться. Ограничение размеров оставляет место вокруг объекта, а внутреннее переполнение допускает содержимое большей высоты. Подпись и кнопка входят в тот же документ, поэтому нуждаются в обычном текстовом масштабе.

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

Рассмотрим конкретный путь читателя. Он изучает подпись под схемой, доходит до кнопки и открывает просмотр. В новом контексте сначала находится действие «Закрыть просмотр», а рядом — заголовок и тот же рисунок. После закрытия человек продолжает движение от кнопки в исходном figure. Мы сохраняем место в последовательности урока; возврат к началу статьи вынуждал бы повторно искать уже прочитанный фрагмент.

У отдельной ссылки другое назначение. Она передаёт SVG обычной навигации браузера, поэтому возвращение связано с историей страницы. Не объединяйте эти действия под одним неопределённым названием «Подробнее»: первое открывает самостоятельный ресурс, второе — временный просмотр внутри документа. Различие полезно ещё до активации и не требует от человека догадаться по внешнему виду иконки.

Слой затемнения тоже не сообщает смысл самостоятельно. Он помогает зрительно отделить окно, но название, видимая закрывающая кнопка и встроенная модель диалога отвечают за управление. В нашем прототипе нажатие на затемнение не объявлено способом закрытия. Читатель может использовать явно показанное действие, а объяснение не обещает отсутствующий обработчик внешней области.

При повторном открытии используется тот же объект, а не новая копия с повторяющимися идентификаторами. Здесь нет формы, незавершённого ввода или выбранной вкладки, поэтому не требуется сохранять дополнительное состояние. Если позднее просмотр станет редактором подписей, это будет другая задача: закрытие, отмена изменения и возврат к результату придётся спроектировать заново. Успех короткого информационного окна нельзя автоматически переносить на составной редактор.

Другой объём содержания

Если внутри появится несколько абзацев или большая таблица, первоначальный фокус на кнопке может оказаться недостаточным ориентиром. APG рекомендует учитывать объём и смысл: иногда уместно начать со статического заголовка с tabindex="-1". Такой вариант требует отдельного решения, а не автоматического добавления всем абзацам остановок Tab.

Также не назначайте длинную подпись целиком как aria-describedby диалога без необходимости. Сложная структура может быть понятнее при последовательном чтении, чем в одном непрерывном объявлении. В нашем примере название определено, а подпись остаётся самостоятельным содержанием.

Будущий маршрут включает открытие клавиатурой, движение по управлению, закрытие кнопкой и Escape, повторное открытие и увеличение текста. Ожидайте возвращения к кнопке схемы и сохранения доступа к подписи. Фактические объявления и поведение потребуется записать отдельно. Теперь необязательное увеличение имеет собственную завершённую модель, сохраняя исходный урок читаемым без неё.