dialog.showmodal()是唯一能触发模态行为的方式,必须调用才能启用遮罩、焦点锁定、esc响应及点击关闭;仅写标签、设open属性或调用show()均无效,且需确保dom就绪、dialog为body直接子元素、手动处理backdrop点击与touch穿透。

dialog.showModal() 是唯一能阻止背景点击的触发方式
不调用 showModal(),就根本不存在 backdrop,背景点击自然不受限。常见错误是只写 <dialog open></dialog> 或调用 show() —— 这两种方式下,dialog 行为等同于一个普通 <div>,Tab 键可穿透、鼠标能点按钮、滚动照常发生。<p>必须确保:<br>
- 脚本在 <code>DOMContentLoaded 后执行,否则 dialog.showModal() 报 Cannot read property 'showModal' of null
- dialog 元素是 的直接子元素,嵌套在 <div class="app"> 之类容器里时,Safari 和部分 Chrome 版本会静默丢弃 backdrop 渲染<br>
- 不手动删 <code>open 属性:调用 showModal() 会自动设 open="",若后续执行 dialog.removeAttribute('open'),弹窗视觉消失(但 DOM 仍在),且 backdrop 永远不会回来
点击 backdrop 关闭需手动监听 click 并判断 target
规范没定义“点击遮罩自动关闭”,::backdrop 是伪元素,无法绑定事件、也不冒泡真实点击来源。用户点 backdrop 时,事件目标(e.target)是 dialog 元素本身,不是 backdrop 节点。
正确做法是监听 dialog 自身的 click 事件,并检查是否点在 dialog 边界外:
dialog.addEventListener('click', e => {
if (e.target === dialog) dialog.close();
});
但 Safari 15.4–17.6 中 e.target === dialog 判断不可靠,需降级为坐标判断:
dialog.addEventListener('click', e => {
const rect = dialog.getBoundingClientRect();
if (
e.clientX rect.right ||
e.clientY rect.bottom
) {
dialog.close();
}
});
移动端 touch 点击穿透必须拦截 touchstart
iOS 和部分安卓浏览器中,touch 操作会合成一次 click 事件。如果用户点在 backdrop 上,这个合成 click 可能穿透到背后元素(比如底部导航栏的按钮被意外触发)。
不能靠 stopPropagation() 或 pointerdown 处理 —— 它们对合成 click 无效。唯一可靠方式是在 dialog 上监听 touchstart 并立即 preventDefault():
dialog.addEventListener('touchstart', e => {
e.preventDefault();
});
注意:
- 必须绑定在 dialog 元素上,而非 document 或 body
- 若需兼容旧版 iOS Safari(style="user-select: none; cursor: none;" 辅助抑制误触
- 此操作不影响 pointerdown 拖拽逻辑,两者可共存
dialog::backdrop 样式控制有严格限制
想改遮罩颜色或透明度,只能用 dialog::backdrop 伪元素;其他方式全是错的:
- 给
dialog设background: rgba(0,0,0,0.5)→ 只染 dialog 自身背景,和 backdrop 无关,还可能遮住内容 - 试图用
dialog::backdrop { transform: scale(1.2); }→ 无效,::backdrop不支持transform、border、padding等大多数属性 - 给 backdrop 加 class 或 id → 不可能,它不是真实 DOM 节点,唯一合法入口就是
dialog::backdrop
真正可用的样式极少:
- background(推荐用 rgba() 控制透明度)
- pointer-events: none(禁用所有交互,慎用,会同时禁掉点击关闭)
- 旧版 Safari(≤15.6)不支持 ::backdrop,需 JS fallback 或提示降级
最易被忽略的一点:showModal() 启动后,焦点自动移到 dialog 内第一个可聚焦元素,但关闭后**不会自动切回触发按钮**。键盘用户按 ESC 关闭后卡在页面顶部,必须手动 triggerBtn.focus(),否则可访问性直接断裂。











