Зачем нужен элемент <dialog>

Источник: «Why you should be using the dialog element»
Диалоговые окна — неотъемлемая часть любого пользовательского интерфейса, будь то веб-интерфейс, мобильный или любой другой, существующий на сегодня.

Хотите получить от пользователя подтверждение? Предоставляете пользователю диалог с возможностью выбора. Хотите получить информацию от пользователя? Используйте диалог с отправляемым полем ввода формы. Существует множество вариантов использования диалогов в пользовательских интерфейсах.

Можно использовать диалоги, установив в проект библиотеки 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>

Вот как это выглядит:

See the Pen

Настройка внешнего вида <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;
}

Вот как будет выглядеть настроенный диалог:

See the Pen

Добавляем интерактивность

В реальных сценариях не стоит использовать диалоговые окна прямо в таком виде. Возможно, потребуется запускать диалог по какому-то действию. Например, при клике на кнопку.

Посмотрите этот пример:

<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>.

Вот всё это в действии:

See the Pen

Обработка пользовательского ввода

Если необходимо обрабатывать вводимые пользователем данные внутри <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>`;
});

Собираем всё воедино:

See the Pen

Ещё несколько доработок

Изменение фона <dialog>

Также можно изменить фон диалога при его открытии с помощью CSS псевдосвойства ::backdrop:

dialog::backdrop {
background-color: #673ab752;
}

Вот как это выглядит:

See the Pen

Закрытие <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();
}
});

Вот как выглядит поведение в действии:

See the Pen

Попробуйте кликнуть за пределами диалога!

В заключение

Вот и всё о встроенном HTML элементе <dialog>, получившем широкую поддержку во всех основных браузерах, включая Safari!

Так что, если при использовании диалоговых окон вам приходится прибегать к помощи элемента <dialog>, это не помешает. Счастливого кодинга!

Дополнительные материалы

Предыдущая Статья

Оптимизация производительности PHP

Следующая Статья

Примеры использования cURL