dialog.show() 是唯一能显示非模态对话框的原生方法,它不创建遮罩、不禁用背景、不拦截 esc、不锁定焦点,行为类似 position: fixed 的 div,需手动实现焦点管理、esc 关闭和点击外部关闭等逻辑。

dialog.show() 是唯一能显示非模态对话框的原生方法
直接写 <dialog open></dialog> 或设置 open 属性,等价于调用 show(),但仍是非模态——它不会禁用背景、不加遮罩、不拦截 Esc、也不锁定焦点。很多人误以为加了 open 就“弹出来了”,其实只是浮在页面上的一层普通元素,和 <div style="position: fixed"> 行为几乎一致。
<p>必须确保 DOM 已就绪再调用:若脚本执行早于元素挂载,会报 <code>Cannot read property 'show' of null。推荐放在 DOMContentLoaded 里,或绑定到按钮点击事件中。
非模态 dialog 不会自动处理焦点和滚动,得自己补
show() 完全不干预用户与页面其余部分的交互。这意味着:
- 背景仍可滚动(
body滚动条照常工作) - Tab 键能穿透 dialog,聚焦到背后表单或链接
- Esc 键完全无响应
- 点击 backdrop(灰蒙层)不会触发任何行为——因为
show()根本不创建 backdrop
如果你需要轻量浮层(比如工具提示、颜色选择器、快捷操作面板),show() 是合适的起点,但所有交互逻辑都得手动实现:监听 keydown 处理 Esc、用 focusin 拦截 Tab、靠 CSS 控制 z-index 和定位。
别指望 click 事件能区分 backdrop 和 dialog 内容
只有 showModal() 才渲染 backdrop;show() 的渲染结果里根本没 backdrop 这个概念。所以你无法监听 “点击 backdrop 关闭” —— 因为它压根不存在。
如果想模拟点击外部关闭,只能靠位置判断:
dialog.addEventListener('click', (e) => {
if (e.target === dialog) {
dialog.close();
}
});
注意:e.target === dialog 成立的前提是用户点在 dialog 元素的空白区域(即 padding 区域),如果 dialog 内有子元素(如 <p></p> 或 <button></button>),点击它们时 e.target 是子元素,不会触发关闭。
Safari 对 show() 的支持基本可靠,但别依赖 Promise
show() 在 Safari 15.4+ 中已稳定支持,且不涉及模态栈,因此没有 Promise 返回值的问题(这点比 showModal() 更省心)。但它返回 undefined,不能链式调用或 await。
如果你需要在显示后立即操作子元素(比如 autofocus 输入框),得用同步方式:
dialog.show();
dialog.querySelector('input')?.focus();
真正容易被忽略的是:非模态 dialog 一旦打开,就完全脱离浏览器的模态管理机制——它不再参与焦点锁、Esc 响应、returnFocus 等任何原生保障。这些都得你自己用 JS 补全,否则 accessibility 会出问题。











