Вы не знаете как должны работать модальные окна
Уверен, многие хоть раз создавали всплывающее модальное окно. Но задумывались ли вы об определении этого компонента? Как он должен работать?
В этом материале я постарался собрать максимально полный свод правил, рекомендаций и примеров реализации по которым модальные окна должны работать.
Я покажу, как просто создавать сложные, удобные, производительные и доступные модальные окна независимо от браузера, платформы, устройства или способа взаимодействия пользователя.
Этот список сформирован на основе спецификаций WAI-ARIA, HTML Living Standard и моего личного опыта. И хотя я буду говорить про веб, большинство правил и рекомендаций применимы для модальных окон где угодно.
Определение модального окна
Модальное окно — это окно наложенное либо на документ, либо на другие окна. При этом, любой контент под модальным окном является недоступным для взаимодействия.
Теги и атрибуты
Интерактивным элементом для открытия диалогового окна должна выступать кнопка. Не Простейшая реализация кнопки открывающая диалог по его id: Для различных диалогов, уведомлений и прочих перекрывающих документ элементов существует тег Так что для этих браузеров нужно подгружать polyfill: Вы, конечно, можете использовать и другой элемент для реализации диалогового окна, например так: но тогда вам придётся самостоятельно реализовывать всё поведение описанное далее. В то время как с Вскользь коснусь внешнего вида. На небольших экранах диалоговое окно должно занимать 100% его размера. Если ваш диалог будет большим: У модального окна, как у любой обычной страницы, должен быть свой заголовок. Короткий, точно описывающий его предназначение. Наличие заголовка намного упрощает восприятие пользователем. Настоятельно рекомендуется использовать для заголовка тег Но просто добавить заголовок в диалоговое окно недостаточно. Их нужно ещё и логически «связать». Сделать это можно с помощью атрибута Теперь, при попадании пользователя в диалоговое окно, в случае с экранным диктором, будет зачитан не только факт наличия диалога, но и его заголовок. Если в вашем диалоговом окне есть какое-то не интерактивное содержание, например, абзац текста, его стоит связать с диалогом подобно заголовку. Иначе, в некоторых случаях программы чтения с экрана не будут озвучивать такой контент. Делается это атрибутом Если в вашем диалоговом окне много контента, тогда стоит обернуть его в один Важно! Заголовок и любые кнопки не относящиеся к содержимому, а служащие для управления диалоговым окном, не должны быть включены в элемент на который указывает Есть другой сценарий, когда содержимое вашего окна состоит из формы без предшествующего ей текста. В таком случае нет необходимости связывать форму с окном: Элементы формы являются интерактивными. И они будут озвучены скринридером, когда пользователь начнёт с ними взаимодействовать. Если скомбинировать и статический текст и форму: Внутри диалогового окна обязана быть кнопка чтобы его закрыть. Не Дополнительно, в зависимости от вашей логики, вы можете позволить пользователю закрыть диалог кликнув за его пределами или нажав Но: Простейшая реализация кнопки закрывающей родительский диалог: А если вы делаете кнопку с иконкой, то не забывайте про подпись, чтобы передать ёё назначение: Во время открытия диалогового окна фокус должен быть перемещён на элемент внутри него. На какой именно — зависит от содержания. В общем случае фокус перемещается на первый интерактивный элемент. Именно так ведет себя нативный Например, для диалога с формой первый интерактивный элемент это первый Но есть и несколько исключений: Управлять куда именно попадёт фокус при открытии модального окна можно с помощью атрибута Особенность модального окна в том, что оно перекрывает собой весь документ не давая возможность с ним взаимодействовать. Чтобы блокировать указатель обычно документ накрывается полупрозрачным блоком. Но этого недостаточно, так как остаётся ещё и навигация клавишами После попадания фокуса в модальное окно пользователь может перебирать интерактивные элементы внутри этого окна, но не должен выходить за его пределы. Другими словами, такое диалоговое окно работает как ловушка для фокуса. Это поведение встроено в При закрытии диалогового окна фокус должен быть перемещён туда, где он был в момент открытия. Это поведение не является частью Но и тут есть одно исключение: если элемент более не доступен, тогда фокус нужно вернуть туда, откуда наиболее логично для пользователя продолжить работу. Предлагаю разобрать на примере. Представим систему из трех диалоговых окон: В примерах ниже я специально пропустил дополнительные атрибуты и элементы, для упрощения кода. Итак, у нас есть стартовая кнопка. По нажатию на неё открывается первый диалог. Фокус автоматически перемещается на первый интерактивный элемент. А закрытие диалога должно возвращать фокус назад. Далее пользователь перемещает фокус на «Условия подписки» и нажимает. Открывается второй диалог поверх первого. Фокус перемещается в него, а возвращаться должен на эту же кнопку в первом диалоге: После закрытия второго диалога ваш JavaScript должен вернуть фокус на кнопку «Условия подписки» в первом. После чего пользователь нажимает кнопку «Подписаться». По условиям нашей задачи открывается третий диалог. Фокус автоматически перемещается в него. А первый диалог закрывается: И вот проблема: третье окно должно вернуть фокус на кнопку в первом, но первое окно больше не доступно. В таких случаях фокус нужно вернуть туда, куда указывал закрытый диалог — на кнопку «Рассылка» с которой пользовать начал. Безусловно, в вашем конкретном случае может быть более логичное поведение для возвращения фокуса. Например, у вас диалог создания новой записи в таблице. В таком случае, может быть логичнее возвращать фокус на только что созданную запить. Помните, как во время установки программы в Windows можно просто нажимать Enter? Так вот это пример хорошей работы с фокусом: каждый раз, при переходе на новый экран в фокус ставится элемент, с которым вы скорее всего будете взаимодействовать — кнопка «Далее» или «Обзор». не
, не любой другой тег. Исключительно
. И касается не только диалоговых окон,
— самый надежный и доступный способ создавать интерактивные элементы на странице.
. Его вы и должны использовать. К огромному сожалению, его поддержка не самая лучшая:
if (!document.createElement('dialog').showModal) {
// Браузер нативно не поддерживает элемент dialog
import('/dist/dialog-polyfill.js') // Подгружаем polyfill
.then(dialogPolyfill =>
document.querySelectorAll('dialog')
.forEach(dialogPolyfill.registerDialog) // Применяем его для всех элементов на странице
)
}
большую часть браузер реализует из коробки.
Внешний вид и содержание
Заголовок обязателен
.-
aria-labelledby
следующим образом: Статический контент должен быть связан с окном
aria-describedby
: aria-describedby
. Они должны быть вынесены отдельно: Интерактивные элементы связывать не нужно
Способы закрыть окно
не
, не любой другой тег. Исключительно
. Это самый надежный способ гарантировать, что любой пользователь сможет закрыть диалоговое окно. Вы же не любите модальные окна которые невозможно закрыть?
Escape
(встроено в из коробки).
Поведение фокуса
При открытии диалога
в браузере. Но нельзя делать сам элемент окна фокусируемым и перемещать фокус на него.
. Если ваше диалоговое окно носит чисто информативный характер, например, уведомление об успешной подписке, тогда первым и единственным элементом будет кнопка закрывающая диалог.
tabindex="-1"
и перемещать фокус на него. Но при этом подходе некоторые программы чтения с экрана могут озвучивать заданный текст дважды: сначала как заголовок и описание окна, а потом как содержание выделенного элемента. autofocus
: Внутри диалога
Tab
/ Shift + Tab
. Также это могут быть клавиши громкости на смартфонах или специальные клавиши на дополнительных инструментах подключенных по USB/Bluetooth. Этот способ навигации тоже должен быть заблокирован. , так что от вас никаких действий не требуется. А вот используя другой элемент с
role="dialog"
его нужно реализовывать самостоятельно средствами JavaScript.При закрытии диалога
и браузер полностью оставляет это на усмотрение разработчика.
Пример
┌►
│
└─
┌►
│
└─
│
└─
┌►
│
└─
┌►
│
└─
│
│
│
└─
┌►
│
│
│
│
│
└─
Подводя итог
и
.
Дополнительные ссылки