Элемент dialog
в HTML является пока еще экспериментальной технологией, хотя и с очень хорошей поддержкой браузерами, и предназначен для отображения диалоговых (всплывающих) или модальных окон.
MDN описывает его как диалоговое окно или другой интерактивный компонент, например, закрываемое предупреждение, инспектор или подокно. Элемент <dialog>
является семантическим элементом в HTML. Он имеет атрибут open
, который показывает браузеру, что модальное окно активно, т.е. должно быть показано. Кроме того, <dialog>
имеет методы и свойства, позволяющие его показывать и прятать.
Итак, тег <dialog>
создаёт диалоговое окно. По умолчанию не показывается на странице (работает свойство display: none
) и может открываться в двух режимах:
- Всплывающее окно — не блокирует взаимодействие со страницей и не имеет перекрывающего фона-подложки.
- Модальное окно — откроется поверх страницы, имеет фоновое затемнение, остальной контент не доступен для взаимодействия.
Кстати, тег <dialog></dialog>
обязательно должен быть парным по спецификации, т.е. иметь закрывающий тег. Внутри него находится содержимое диалогового окна с произвольной разметкой, в которой можно использовать заголовки, абзацы, div
, img
и формы. Для элемента <dialog>
нельзя задавать атрибут tabindex
.
В примере ниже вы сразу увидите открытое всплывающее окно, т.к. у него в разметке прописан атрибут open
. Однако у вас не получится закрыть его нажатием на клавишу ESC
, да и задний фон-подложку, перекрывающий остальные элементы, вы в этом случае не увидите. Закрыть окно можно только кликом на кнопку внутри этого диалогового окна.
See the Pen HTML 5.2 dialog element by Elen (@ambassador) on CodePen.
Второе окно открывается методом showModal()
с помощью JavaScript-кода. И здесь уже присутствует фон, созданный с помощью псевдоэлемента ::backdrop
. Закрывается окно нажатием ESC
(предустановленное поведение) или кликом в любом месте, что уже сделано за счет JavaScript.
Стиль модального окна для элемента <dialog>
Итак, для нашего модального окна, созданного с помощью элемента <dialog>
, можно задавать любые стили в виде padding
, border
, width (max-width, min-width)
, height (max-height, min-height)
, color, line-height
и т.д. По умолчанию в браузере Chrome прописаны такие стили:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | dialog { display: none; position: absolute; inset-inline-start: 0px; inset-inline-end: 0px; width: fit-content; height: fit-content; background-color: canvas; color: canvastext; margin: auto; border-width: initial; border-style: solid; border-color: initial; border-image: initial; padding: 1em; } |
При открытии диалогового окна стили меняются:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | dialog:-internal-dialog-in-top-layer { position: fixed; inset-block-start: 0px; inset-block-end: 0px; max-width: calc(100% - 2em - 6px); max-height: calc(100% - 2em - 6px); user-select: text; visibility: visible; overflow: auto; } dialog[open] { display: block; } dialog:modal { overlay: auto !important; } |
То есть по умолчанию вы увидите открытое диалоговое окно с черной рамкой и черным текстом, белым цветом фона, небольшими внутренними отступами и отступами снаружи, а также размером по содержимому.
Те свойства, которые вы хотите изменить, вам нужно будет задать в стилях самостоятельно.
Кроме того, мы можем стилизовать для диалога псевдоэлемент ::backdrop
(подложка), хотя браузеры применяют почти прозрачный серый фон по умолчанию (можно посмотреть на него в примере с заполнением формы):
1 2 3 4 5 | dialog:-internal-dialog-in-top-layer::backdrop { position: fixed; inset: 0px; background: rgba(0, 0, 0, 0.1); } |
Вы можете написать в своих стилях:
1 2 3 4 5 6 7 8 | /* Подложка показывается только тогда, когда диалоговое окно открыто с помощью метода dialog.showModal() */ dialog::backdrop { background-color: rgba(0,0,0,.6); } /* или */ dialog::backdrop { background: conic-gradient(rgba(128,128, 128, .5), rgba(255, 255,255,.7), var(--dark-blue), black); } |
Плюс использовать анимацию типа animation
и @keyframes
(пример выше) . Для использования свойства transition
придется изменить display: none/block
на visibility: hidden/visible
при закрытом и открытом диалоговом окне.
Использование JavaScript
Методы элемента <dialog>
show()
- открывает всплывающее окно, добавляет атрибутыopen
иaria
. В этом случае окно является диалоговым, или всплывающим, но не перекрывает контент (он доступен для взаимодействия ().- modal = "false" showModal()
- позволяет открыть диалоговое окно, причем с отображением заднего фона (доступен для стилизации псевдоэлемент::backdrop
) плюс можно использовать клавишуESC
для закрытия этого диалогового окна. В этом случае<dialog>
открывается в режиме «модального окна». К нему добавляются атрибутыopen
иaria
- modal = "true" close()
- позволяет закрыть диалоговое или модальное окно.
События:
close
- наступает при закрытии диалогового окна.cancel
- событие, которое наступает при нажатии кнопкиESC
.
cancel
, а затем событие close
. Это может быть полезно, если мы хотим избавить пользователя от случайного нажатия клавиши ESC
, сначала предупредив, что изменённые данные не сохранятся, и только при повторном нажатии закрывать окно. 1 2 3 4 5 6 7 8 9 10 11 12 13 | // Внутри функции, которая открывает диалоговое окно с помощью dialog.showModal() const dialog = document.querySelector("dialog"); const openDialog = () => { dialog.showModal(); // ... dialog.addEventListener("keydown", (e) => { if (e.key === "Escape") { e.preventDefault(); } }); }; |
Блокируем прокрутку в body при открытии <dialog>
Давайте используем открытие модального окна для того чтобы отменить прокрутку внутри body
, которая есть почти во всех примерах в этой статье. Мы будем добавлять класс modal-open
для body
при клике на кнопку, которая открывает диалоговое окно:
1 2 3 4 | document.getElementById('launch').addEventListener('click',e => { dialogElement.showModal(); document.body.classList.add('modal-open'); }); |
Нам нужно добавить стиль, фиксирующий ширину прокрутки для html
и body
, а в классе modal-open
у нас будет одно правило:
1 2 3 4 5 6 7 | html, body { scrollbar-gutter: stable; } .modal-open { overflow: hidden; } |
Затем мы будем убирать этот класс при обработке события close
для диалогового окна:
1 2 | dialogElement.addEventListener('close', () => document.body.classList.remove('modal-open')) |
Давайте посмотрим, как работает это на практике:
See the Pen HTML element dialog by Elen (@ambassador) on CodePen.
Свойство returnValue
Свойство returnValue
позволяет нам задать некую реакцию после действий по закрытию диалогового окна в виде элемента <diallog>
. Если кнопкам в форме задать атрибут value
, то при закрытии диалога это значение будет присваиваться в dialog.returnValue
.
Пример использования свойства returnValue
See the Pen HTML element dialog by Elen (@ambassador) on CodePen.
И второй пример со слегка изменённым кодом:
See the Pen HTML element dialog by Elen (@ambassador) on CodePen.
Формы внутри элемента <dialog>
Элементы <form>
могут интегрироваться с диалогом с помощью добавления к ним атрибута method="dialog"
. Когда отправляется такая форма, то диалоговое окно закрывается с returnValue
, равным value
нажатой кнопки типа submit
. Напомню, что в случае использования тега <button>
внутри тега <form>
атрибут type="submit"
автоматически присваивается тегу <button>
.
Мы уже использовали такой подход для получения value
из кнопок в примере выше. Давайте посмотрим что мы можем сделать при добавлении в модальное диалоговое окно формы для входа на сайт.
В примере ниже мы будем предоставлять право пользователю ввести логин и пароль для "входа на сайт" в форме, а затем при закрытии окна выведем либо под каким логином он "вошел", либо сообщение о том, что поля-таки надо заполнить.
See the Pen Simple Login Form Template by Elen (@ambassador) on CodePen.
Закрываем диалоговое окно по клику на псевдоэлемент ::backdrop
Для того чтобы не писать код закрытия окна при клике на кнопки, поместите их в тег <form method="dialog"></form>
. Для того чтобы закрыть окно при клике на ::backdrop
, нужно обработать клик по самому элементу <dialog>
, т.к. по псевдоэлементу клик обработать не удастся. Для того чтобы клик по отступам вокруг текста в диалоговом окне не приводил к его закрытию, нужно убрать padding
вокруг текста внутри элемента <dialog>
, обернуть весь текст внутри <dialog>
в какой-нибудь контейнер, для которого нужно задать padding
, и дописать код на JavaScript, который проверяет, где был сделан клик.
В этом коде нужно понимать, что является target
и currentTarget
при клике на элементе в процессе делегирования событий. target
- это целевой объект под курсором мыши. При наличии текста в диалоговом окне вы будете кликать то по заголовку, то по абзацу, то по кнопкам, то по div
-у-обертке, но не по самому элементу <dialog>
. И только при клике на ::backdrop
вы будете кликать по элементу <dialog>
. В этом случае target
совпадет с currentTarget
, и модальное окно будет закрыто.
See the Pen Click Dialog Backdrop by Elen (@ambassador) on CodePen.
Примерно такой же по сути код вы найдете в первом примере.
Аспекты доступности элемента <dialog>
Чтобы обеспечить доступность вашего модального окна в виде элемента <dialog>
, вы уже получаете фокус на первом интерактивном элементе после открытия диалогового окна помощью метода dialog.showModal()
. Взаимодействие с другим содержимым на странице будет заблокировано, пока диалоговое окно активно, без вашего участия, т.е. какого-либо кода. Кроме того, диалоговое окно закрывается нажатием клавиши ESC
. Все это идет «бесплатно» вместе с элементом <dialog>
.
В отличие от использования элемента div
в качестве оболочки вместо семантически правильного элемента <dialog>
, вам не обязательно добавлять такие атрибуты, как role="dialog"
и aria-modal="true
.
Вы также можете добавить следующие атрибуты:
- Поместите метку на элемент диалога — например,
<dialog aria-label="Вы готовы принять участие в проекте?">
или используйтеaria-labelledby="dialog_title"
, если вы хотите сослаться на идентификатор другого элемента внутри разметки диалога, который представляет заголовок. - Если диалоговое сообщение требует дополнительной информации, которая уже может быть видна внутри диалогового окна, вы можете дополнительно сослаться на этот текст в атрибуте
aria-describedby
или сформулировать описание только для программ чтения с экрана внутри сообщения.aria-description
- Добавьте полифилл для поддержки старых браузеров строкой
import "dialogPolyfill from "https://cdn.skypack.dev/dialog-polyfill@0.5.6";
.
Пример:
See the Pen Click Dialog Backdrop by Elen (@ambassador) on CodePen.
Другие способы создания модальных окон (CSS, JS, Bootstrap, плагины) вы найдете в отдельной статье.