Модальное окно на HTML без JS-библиотек: тег dialog

<dialog> - тег для модального окна: сам по себе он ничего не блокирует, но метод showModal() из JavaScript даёt настоящую модальность - подложку, блокировку остальной страницы и закрытие по Esc, без единой JS-библиотеки. А form method="dialog" умеет закрывать окно и передавать результат клика без единой строчки лишнего кода.

Заметь: нажми "Удалить файл" - откроется настоящее модальное окно (фон затемнён, страница за ним неактивна). Нажми "Отмена" или "Удалить" - окно закроется само, а текст под кнопкой обновится значением returnValue, которое взялось из атрибута value нажатой кнопки. Ни одной строчки кода для управления фокусом или блокировкой страницы писать не пришлось.

Открыть модальное окно: showModal()

<dialog> - обычный контейнер, если его просто добавить в разметку. Настоящую модальность даёт только вызов метода showModal() из JavaScript: страница за окном становится по-настоящему неактивной (клик мимо окна ничего не делает), появляется затемнённая подложка (её можно оформить через псевдоэлемент ::backdrop), а нажатие Esc закрывает окно. Если вместо showModal() просто написать атрибут open в разметке (или вызвать более слабый метод show()), получится немодальное окно: страница вокруг остаётся кликабельной, Esc его не закрывает, а сам браузер не воспринимает это как :modal. Разница не в том, как окно выглядит, а именно в поведении - оба варианта окно нарисуют одинаково, но только showModal() реально блокирует остальную страницу. Дописывать тегу dialog атрибут tabindex не нужно - сам по себе он не интерактивен, фокус при showModal() автоматически уходит на первый доступный элемент внутри (или на элемент с autofocus).

<script>document.getElementById("d").showModal()</script> - настоящее модальное окно: страница за ним заблокирована.

<dialog open><p>Текст</p></dialog> - без showModal() это немодальное окно: страница вокруг по-прежнему кликабельна, Esc не действует.

Закрыть правильно: method=dialog и returnValue

Форма с атрибутом method="dialog" внутри dialog закрывает окно при отправке без обращения к серверу и без перезагрузки страницы, а свойство dialog.returnValue получает значение атрибута value нажатой кнопки - это можно прочитать в обработчике события close. Нажатие Esc тоже закрывает окно, но returnValue при этом не обновляется. Отменить закрытие по Esc можно только через отдельное событие cancel, которое срабатывает непосредственно перед закрытием - вызов preventDefault() внутри его обработчика оставит окно открытым. Частое заблуждение - что клик по затемнённой подложке снаружи окна закроет его сам по себе: по умолчанию это не так, для такого поведения нужен либо свой код, либо новый атрибут closedby="any", который на момент написания урока ещё не поддерживается частью основных браузеров. Поэтому в диалоге всегда должна быть явная видимая кнопка закрытия - иначе пользователю без физической клавиши Esc (например, на сенсорном экране) будет попросту нечем закрыть окно.

<form method="dialog"><button value="ok">ОК</button></form> - закрывает окно, returnValue станет "ok".

<dialog><p>Все данные будут удалены</p></dialog> - нет ни одной видимой кнопки закрытия, на сенсорном экране без Esc окно нечем закрыть.

Частые ошибки новичков

ОшибкаЧто происходитКак исправить
Открывать dialog только атрибутом open в разметке Получается немодальное окно: страница вокруг кликабельна, Esc не закрывает Вызывать showModal() из JavaScript для настоящей модальности
Не давать явную видимую кнопку закрытия На сенсорных устройствах без физической Esc окно нечем закрыть Всегда добавлять видимую кнопку "Закрыть" или "Отмена"
Ожидать, что клик по затемнённой подложке закроет окно сам по себе По умолчанию клик снаружи окна ничего не делает Закрывать явно - form method=dialog, close() или (в перспективе) атрибут closedby
Добавлять tabindex самому тегу dialog Тег не интерактивен, атрибут лишний и может сломать порядок фокуса Фокус ставить на кнопки или поля внутри dialog, не на сам тег
Пытаться остановить закрытие по Esc произвольным кодом на keydown Встроенное закрытие по Esc отменяется только через событие cancel Слушать cancel на самом dialog и вызывать preventDefault() в нём

Шпаргалка

Тег/методЧто нужно знать
<dialog>Контейнер окна; для настоящей модальности нужен JS-вызов showModal()
showModal()Модальное окно: подложка ::backdrop, блокировка страницы, закрытие по Esc
show() / голый openНемодальное окно: страница активна, Esc не закрывает
form method="dialog"Закрывает окно без отправки на сервер, returnValue = value нажатой кнопки
cancel + preventDefault()Единственный способ отменить закрытие по Esc

Коротко: отвечаем на вопросы

Чем showModal() отличается от простого атрибута open?

showModal() даёт настоящую модальность: остальная страница блокируется от кликов, появляется затемнённая подложка, а Esc закрывает окно. Голый атрибут open (или метод show()) даёт немодальное окно - страница вокруг остаётся полностью активной, а Esc на него не действует.

Закроется ли окно, если кликнуть по затемнённой подложке снаружи?

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

Что делает form method="dialog" внутри окна?

Закрывает dialog без отправки данных на сервер и без перезагрузки страницы. Значение атрибута value нажатой кнопки записывается в dialog.returnValue, которое можно прочитать в обработчике события close.

Можно ли отменить закрытие окна по нажатию Esc?

Да, но не обработкой keydown, а через отдельное событие cancel, которое срабатывает непосредственно перед закрытием. Вызов preventDefault() в его обработчике оставит окно открытым.

Категория
Поделиться в социальных сетях

Отправить комментарий

0 Комментарии

Отправить комментарий (0)

#buttons=(Хорошо) #days=(20)

Мы используем файлы cookie для улучшения работы сайта. Подробнее
Хорошо
×