dialog 必须作为 body 的直系子元素,用 showModal() 触发模态行为;open 属性无效,需 autofocus、标题、关闭控件,并手动监听 backdrop 点击与 Esc 键。
body 下,否则居中和 backdrop 会失效浏览器把 dialog 当作顶层浮层处理,一旦嵌套在 <form></form>、<div style="position: relative"> 或任何设置了 <code>transform/overflow 的容器里,它的自动居中逻辑就会被破坏,dialog::backdrop 也可能不渲染或错位。
常见错误结构:<main><dialog></dialog></main> 或 <div class="modal-wrapper"><dialog></dialog></div>。
✅ 正确写法:确保 dialog 是 body 的直系子元素。如果 JS 动态创建,务必用 document.body.appendChild(dialog) 挂载,而不是 append 到某个 wrapper 里。
showModal() 触发,别靠 open 属性硬开open 属性只控制初始显隐,它不会加 backdrop、不锁背景、不响应 Esc、也不触发 close 事件。用户点了遮罩或按了 Esc,什么都不会发生。
立即学习“前端免费学习笔记(深入)”;
真正模态行为只来自 dialog.showModal() —— 它自动聚焦、禁用背景交互、生成 dialog::backdrop,并让 Esc 关闭生效。
注意点:
dialog 已在 DOM 中(不能在 head 里提前执行 JS)dialog.open = true 或 dialog.setAttribute('open', '') 来“模拟打开”dialog.show() 是非模态浮层,适合 tooltip 类场景,不是对话框autofocus、标题和明确关闭控件没有可聚焦元素时,showModal() 可能焦点落在 body 上,键盘用户会直接跳出 dialog;没有标题则屏幕阅读器无法识别内容意图。
最小可用结构示例:
<dialog id="confirm" aria-labelledby="confirm-title"> <h3 id="confirm-title">确认删除</h3> <p>该操作不可撤销</p> <button autofocus>确定</button> <button type="button">取消</button></dialog>
关键项:
autofocus 元素(推荐第一个按钮)<h2> 或 <h3> 标题 + aria-labelledby 关联type="button",避免触发表单提交dialog,也别只放纯文本没交互控件dialog::backdrop 默认不响应点击,这是规范行为,不是 bug。指望“点背景就关”必须自己加逻辑。
基础监听写法:
dialog.addEventListener('click', e => { if (e.target === dialog) dialog.close();});
但 Safari 15.4–16.3 存在兼容问题:某些情况下 e.target 始终是 body,判断失效。这时得 fallback:
e.clientX / e.clientY 是否在 dialog.getBoundingClientRect() 区域外<div class="backdrop"> 手动模拟(配合 dialog.show())dialog-polyfill 统一处理最易忽略的点:很多人只给关闭按钮绑 click,却忘了 backdrop 点击和 Esc,导致键盘/触屏用户被困在 dialog 里。