介绍 Dialog 元素

尽管 alertconfirmprompt 等 JavaScript 方法很方便,但由于它们的脚本阻塞行为,因此不推荐使用。这就是为什么我们过去几年与其他浏览器厂商合作,推动 <dialog> 规范的改进。其中最重要的讨论涉及可访问性。

您可以在网络上找到更复杂的用例,例如支付对话框。它们目前通过 Bootstrap 等框架的自定义解决方案来解决。不幸的是,它们使用不方便,并且并非总是可访问的。我们认为网络值得为这些用例提供一个简单且无 Bug 的解决方案。正因如此,Safari 技术预览版 134 和 Safari 15.4 Beta 引入了 <dialog> 元素!

如何使用 <dialog>

让我们创建一个简单的确认对话框

<dialog id="confirmation-dialog">
    <h1>Do you want to delete everything?</h1>
    <p>You will lose all your data.</p>
    <button id="cancel-delete">Cancel</button>
    <button id="confirm-delete">Delete!</button>
</dialog>

对话框默认是隐藏的。我们可以使用 showModal() 方法来显示对话框。当对话框显示时,可以使用 close() 方法将其关闭。

这是一个示例

<button id="delete">Delete everything</button>
<p id="result"></p>
<script>
let dialog = document.getElementById("confirmation-dialog");
let result = document.getElementById("result");

// Show the dialog when clicking "Delete everything"
document.getElementById("delete").addEventListener("click", function() {
    dialog.showModal();
});

document.getElementById("cancel-delete").addEventListener("click", function() {
    dialog.close();
    result.textContent = "Canceled!";
});
document.getElementById("confirm-delete").addEventListener("click", function() {
    dialog.close();
    result.textContent = "Deleted!";
});
</script>

请注意,对话框打开后会获得一个 open 属性,这可能对样式设置有用。但是,不建议手动切换此属性来显示或隐藏对话框,因为浏览器可能会失去对话框状态的跟踪,并且不会为可访问性执行适当的焦点调整。

确认对话框示例

模态和非模态对话框

在上一个示例中,使用 showModal() 方法创建了一个模态对话框。用户交互被锁定在模态对话框内部,外部内容无法被点击、聚焦、选择、编辑,也无法被辅助功能工具看到。模态对话框的另一个特性是它们能够出现在网页中所有其他内容之上,无论其他元素的 z-index 值是多少。

非模态对话框也存在,可以使用 show() 方法调用。与模态对话框不同,它们仍然允许与周围内容进行交互。一个用例可能是一个文档编辑器的页面内查找对话框,在这种情况下,您仍然希望允许用户与文档的其余部分进行交互。

将表单与 <dialog> 结合使用

对话框中的表单可用于向用户请求信息,例如需要送货地址或付款详情时。

与传统的 <form> 不同,传统的 method="get""post" 表示表单数据发送到服务器,而使用 <form method="dialog"> 则会导致表单提交以关闭对话框并将 returnValue 属性设置为提交按钮的值。这可以省去您编写自定义代码的麻烦,同时为您的网页提供正确的语义。

我们可以使用这个新功能来简化最初的示例

<dialog id="confirmation-dialog">
    <form method="dialog">
        <h1>Do you want to delete everything?</h1>
        <p>You will lose all your data.</p>
        <button type="submit" value="Canceled!">Cancel</button>
        <button type="submit" value="Deleted!">Delete!</button>
    </form>
</dialog>

<button id="delete">Delete everything</button>
<p id="result"></p>

<script>
let dialog = document.getElementById("confirmation-dialog");

document.getElementById("delete").addEventListener("click", function() {
    dialog.showModal();
});

dialog.addEventListener("close", function() {
    document.getElementById("result").textContent = dialog.returnValue;
});
</script>

请注意这里使用了 close 事件,它是 <dialog> 特有的。

带有表单的确认对话框示例

样式

您可能从之前的示例中注意到的对话框后面半透明的框是 ::backdrop 伪元素。默认情况下,它的样式覆盖整个视口。像对话框本身一样,您可以使用 CSS 为背景(backdrop)设置样式。如果您想添加淡入效果,也可以使用动画。

请注意,背景仅在模态对话框中显示。

这是一个示例

<dialog>
    <h1>This is a pretty dialog</h1>
    <p>The backdrop animates!</p>
</dialog>

<button onclick="document.querySelector('dialog').showModal()">Show the dialog</button>

<style>
dialog {
    box-shadow: 0 2px 5px rgba(0,0,0,0.3);
    border: none;
    border-radius: 10px;
}

dialog::backdrop {
    background: linear-gradient(rgba(0,0,0,0.1), rgba(0,0,0,0.4));
    animation: fade-in 1s;
}

@keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
</style>

这是一个漂亮的对话框

背景有动画效果!

漂亮的对话框示例,已设置样式和动画

可访问性

对于可访问性工具,<dialog> 元素等同于 role="dialog"。此外,模态对话框的行为类似于带有 aria-modal="true" 的元素。

用户可以在桌面浏览器上使用“Escape”键关闭模态对话框。这将触发一个您可以拦截的 cancel 事件。如果打开了多个模态对话框,则最后显示的那个将被关闭。

还可以通过向相关元素添加 autofocus 属性来指定在打开对话框时最初要聚焦的元素。

浏览器支持

下一步

在这篇文章中,我们介绍了 <dialog> 的基础知识及其相关功能。以下是围绕此元素的后续步骤:

作为 互操作性 2022 (Interop 2022) 工作的一部分,我们正在努力使该元素与其他浏览器厂商实现互操作。其中一个主要讨论围绕 初始焦点行为,以就当没有带有 autofocus 属性的元素时,哪些元素应该默认获得焦点达成一致。

作为实现 <dialog> 的一部分,我们还在 inert 属性方面取得了进展,以实现在浏览器之间的互操作行为。它目前默认禁用且尚未标准化,但您可以在 Safari 技术预览版的“开发”菜单中从“实验性功能”菜单中启用“inert 属性”来测试它。

如有任何疑问,请随时通过 Twitter 联系 @therealntim。要报告任何问题,请提交 Bug,并阻止 Bug 84635