Элемент 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>, как диалогового окна, его еще можно открывать при необходимости показа каких-то элементов в виде информационных таблиц. Например, на странице товара в интернет-магазине нужно отобразить таблицу размеров или особенности оплаты в рассрочку. Тут тоже отлично справится диалоговое окно, но с элементами стилизации.
Пример кода вы найдете ниже.
See the Pen Dialog as Popover by Elen (@ambassador) on CodePen.
Кроме элемента <dialog> в этом коде вы найдете <div> с атрибутом popover, который позволяет отобразить всплывающую подсказку при клике на кнопке-ссылке с текстом "Оплата в рассрочку".
Аспекты доступности элемента <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, плагины) вы найдете в отдельной статье.