dialog元素必须直接位于body下,不可嵌套;按钮需手动调用close()并传参;safari≤15.3需手动设置dialog::backdrop背景色;焦点需手动管理,不能依赖autofocus。

dialog 元素必须是 的直接子元素
这是最容易被忽略的硬性限制。如果你把 <dialog></dialog> 套在 <div id="app"> 里(比如 Vue/React 的根容器),Safari 和部分 Chrome 版本会直接禁用 backdrop、Esc 关闭、点击外部关闭等模态行为——不是“不好使”,而是根本没激活。
<p>正确写法只有一种:<code> 下第一层就放 <dialog id="confirmDialog"></dialog>,不能嵌套,不能包裹,不能用 v-if 或 {show && <dialog>}</dialog> 动态渲染(得用 open 属性或 showModal() 控制显隐)。
- 错误示例:
<div id="root"><dialog>...</dialog></div> - 正确示例:
<dialog id="confirmDialog">...</dialog><div id="app">...</div> - 若用框架,需确保
<dialog></dialog>挂载到document.body,而不是组件内部 DOM 树
确认/取消按钮必须手动绑定 close(),不能依赖 confirm 属性
<dialog confirm></dialog> 或 <dialog type="confirm"></dialog> 都是无效写法,浏览器会静默忽略。原生 <dialog></dialog> 不提供任何按钮逻辑,所有行为都得靠 JS 显式调用 close() 并传参。
关键点在于:返回值不是布尔,而是你传进去的字符串,且只在 close 事件后通过 dialog.returnValue 读取。
- 确认按钮:
confirmBtn.addEventListener('click', () => dialog.close('confirmed')) - 取消按钮:
cancelBtn.addEventListener('click', () => dialog.close('canceled')) - 用户按 Esc 或点 backdrop 关闭时,
dialog.returnValue是空字符串"",不是"canceled" - 别监听
event.returnValue——它不存在;也别在close事件回调里读event.target.returnValue,直接读dialog.returnValue
遮罩层 ::backdrop 在 Safari ≤15.3 必须手动设背景色
现代 Chrome/Firefox 中 dialog::backdrop 默认有半透明灰背景,但 Safari 15.3 及更早版本压根不渲染它,导致点击外部无反应、视觉上像普通弹层——这不是 bug,是规范实现差异。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
兼容写法很简单,加一行 CSS:
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
}
注意:这个规则必须生效(没被 scoped 或 shadow DOM 隔离),且不能写成 dialog::backdrop { display: block; } 这类无效声明。
- 不要用
!important强覆盖,优先检查选择器作用域 - 如果用了 CSS-in-JS 或 Shadow DOM,得把该样式注入全局
<style></style>标签 - 测试时务必在 Safari 15.3 真机或模拟器中验证点击 backdrop 是否真能关闭
焦点管理必须手动补全,否则键盘操作不可靠
<dialog></dialog> 天然捕获焦点,但只在首次打开时生效。如果弹窗内没有可聚焦元素(比如只有纯文本),或者用户 tab 到弹窗外再切回来,焦点就会丢失,导致 Esc 失效、屏幕阅读器无法识别。
安全做法是:确保弹窗内至少有一个 <button></button> 或 <input>,并在打开后主动 focus() 到第一个交互元素;关闭前最好也 focus() 回触发按钮。
- 给
<dialog></dialog>加tabindex="-1",防止它本身被 tab 进去 - 打开后执行:
dialog.showModal(); dialog.querySelector('button, input').focus(); - 关闭前保存触发源:
const trigger = event.target; dialog.addEventListener('close', () => trigger.focus(), { once: true }); - 别依赖
autofocus属性——它在showModal()后不一定及时生效
<dialog></dialog> 当成 alert() 的替代品来用:期待它自动带按钮、自动区分“确认/取消”、自动处理焦点和语义。它只是个容器,骨架干净,但每一块肌肉都得你亲手接上。










