Зачем нужен элемент <dialog>
Хотите получить от пользователя подтверждение? Предоставляете пользователю диалог с возможностью выбора. Хотите получить информацию от пользователя? Используйте диалог с отправляемым полем ввода формы. Существует множество вариантов использования диалогов в пользовательских интерфейсах.
Можно использовать диалоги, установив в проект библиотеки JavaScript, доступные для любого фреймворка, используемого вами. Например, если проект на React, можно использовать react-aria-modal
. Для Angular можно использовать ngx-smart-modal
. Или, если работаете со старым добрым jQuery, можете использовать jquery-modal
.
Сторонние библиотеки — это здорово, но они несут в себе дополнительные накладные расходы, которые не только добавляют сложность проекту, но и увеличивают общий размер пакета приложения.
Кроме того, придётся установить целую библиотеку со всеми её плюсами и минусами, даже если собираетесь использовать только некоторые функции этой библиотеки. На мой взгляд, это не очень хорошо.
Именно здесь на помощь приходит встроенный в браузер элемент <dialog>
. Это всё, что нужно в диалоге, и теперь, когда Safari добавил поддержку элемента <dialog>
, начиная с версии 15.4, нет никаких оправданий, чтобы не использовать их в продакшене!
Что такое элемент <dialog>
<dialog>
— HTML элемент, представляющий диалоговое окно, такое как предупреждение, подтверждение, инспектор или подокно.
Очень простой элемент <dialog>
будет выглядеть следующим образом:
<dialog>
Hey, this is an HTML dialog!
</dialog>
Поскольку <dialog>
не будет виден по умолчанию, необходимо передать свойство open
, чтобы сделать его видимым:
<dialog open>
Hey, this is an HTML dialog!
</dialog>
Вот как это выглядит:
Настройка внешнего вида <dialog>
Возможно, стоит задаться вопросом, откуда взялась эта чёрная рамка в примере? Ответ заключается в том, что каждый пользовательский агент (браузер) применяет к элементу <dialog>
стиль по умолчанию, который выглядит примерно так:
dialog[open] {
display: block;
}
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;
}
Дизайн диалога можно легко настроить. Например, если хотите убрать стандартную рамку и добавить вместо неё падающую тень, а также ещё несколько дополнительных элементов, вот как это можно сделать:
dialog {
border: none;
box-shadow: #00000029 2px 2px 5px 2px;
border-radius: 10px;
padding: 30px;
background-color: pink;
font-family: sans-serif;
font-size: 20px;
font-weight: bold;
}
Вот как будет выглядеть настроенный диалог:
Добавляем интерактивность
В реальных сценариях не стоит использовать диалоговые окна прямо в таком виде. Возможно, потребуется запускать диалог по какому-то действию. Например, при клике на кнопку.
Посмотрите этот пример:
<dialog id="demoDialog">
Hey there! This is a dialog
<button id="closeDialog">Close dialog</button>
</dialog>
<menu>
<button id="openDialogButton">Open dialog box</button>
</menu>
Как видите, есть диалог с идентификатором demoDialog
. Теперь, если нужно открыть этот диалог, кликнув по кнопке Open dialog box, вот как это можно сделать:
var openDialogButton = document.getElementById('openDialogButton');
var demoDialog = document.getElementById('demoDialog');
var closeDialog = document.getElementById('closeDialog');
// "Open dialog box" кнопка открывающая <dialog>
openDialogButton.addEventListener('click', function () {
if (typeof demoDialog.showModal === "function") {
demoDialog.showModal();
} else {
console.log("The <dialog> API is not supported by this browser");
}
});
closeDialog.addEventListener('click', function() {
demoDialog.close();
})
Чтобы открыть диалог, можно программно вызвать метод showModal()
на экземпляре <dialog>
с помощью JavaScript. Но перед этим было бы неплохо проверить, поддерживает ли браузер <dialog>
или нет, проверив, существует ли showModal
в объекте диалога.
Можно вызвать метод close()
на Close dialog, чтобы закрыть <dialog>
.
Вот всё это в действии:
Обработка пользовательского ввода
Если необходимо обрабатывать вводимые пользователем данные внутри <dialog>
, можно определить внутри него <form>
и установить её метод как "dialog".
<dialog id="collectEmail">
<form method="dialog">
<p>
<label>
Email:
<input type="email" name="email" id="email" placeholder="Enter your email">
</label>
</p>
<menu>
<button value="cancel">Cancel</button>
<button id="confirmBtn" value="default">Confirm</button>
</menu>
</form>
</dialog>
<menu>
<button id="openEmailDialog">Submit user's email</button>
</menu>
<output aria-live="polite"></output>
Когда форма будет отправлена, диалог автоматически закроется, и тогда можно будет обработать введённое значение в событии диалога close
следующим образом:
collectEmail.addEventListener('close', function() {
let email = document.getElementById('email').value;
outputBox.innerHTML = `User's email: <b>${email}</b>`;
});
Собираем всё воедино:
Ещё несколько доработок
Изменение фона <dialog>
Также можно изменить фон диалога при его открытии с помощью CSS псевдосвойства ::backdrop
:
dialog::backdrop {
background-color: #673ab752;
}
Вот как это выглядит:
Закрытие <dialog>
при клике за его пределами
По умолчанию, когда <dialog>
открыт, он не может быть закрыт при клике за пределами области диалога. Если необходимо внедрить такое поведение, можно получить DOMRect
диалога с помощью функции getBoundingClientRect()
при клике на диалоге.
Затем можно получить координаты клика и проверить, не попадает ли он за пределы прямоугольника диалога. И на основании этого можно вызвать событие close()
диалога следующим образом:
collectEmail.addEventListener("click", event => {
const rect = collectEmail.getBoundingClientRect();
if (event.clientY < rect.top || event.clientY > rect.bottom ||
event.clientX < rect.left || event.clientX > rect.right) {
favDialog.close();
}
});
Вот как выглядит поведение в действии:
Попробуйте кликнуть за пределами диалога!
В заключение
Вот и всё о встроенном HTML элементе <dialog>
, получившем широкую поддержку во всех основных браузерах, включая Safari!
Так что, если при использовании диалоговых окон вам приходится прибегать к помощи элемента <dialog>
, это не помешает. Счастливого кодинга!