close() 不自动恢复焦点,需手动将焦点交还触发按钮;否则键盘用户会跳出弹窗继续遍历页面。必须在 close 事件回调中调用 triggerBtn.focus(),若按钮已销毁则需提前缓存引用或回溯 document.activeElement。

原生 <dialog> 是目前最省心的方案,但必须用 showModal()、close(),且不能跳过焦点管理——否则键盘用户和屏幕阅读器会直接“掉出”弹窗。
为什么点击遮罩层关闭后焦点会丢失
浏览器调用 close() 时只隐藏弹窗,不自动恢复焦点。若没手动把焦点交还给触发按钮,键盘用户按完 Esc 或点完遮罩后,焦点就悬在 body 或上一个随机元素上,Tab 键继续往后走,可能直接跳到页脚链接或广告位。
常见错误是只写 dialogEl.close() 就结束,没配对处理焦点:
- 打开时:确保
dialogEl.showModal()后,第一个可聚焦子元素获得焦点(dialogEl.querySelector('button, input, [tabindex="0"]')?.focus()) - 关闭时:监听
close事件,在回调里把焦点设回触发按钮(triggerBtn.focus()) - 若触发按钮已销毁(比如是动态渲染的列表项),需提前缓存其引用或用
document.activeElement回溯
<dialog> 的 backdrop 点击关闭为何有时不生效
不是所有 <dialog> 都能点背景关——只有 showModal() 创建的模态态才附带可交互的 backdrop;show() 不生成 backdrop,remove() 或 hidden=true 会破坏模态栈,导致后续无法再打开。
立即学习“前端免费学习笔记(深入)”;
还要检查三件事:
-
<dialog>必须是<body>的**直接子元素**,嵌套在<div id="app">里会导致 Safari backdrop 渲染失败 - Safari ≤ 15.6 不支持
dialog::backdrop样式,需手动加 CSS:dialog::backdrop { background: rgba(0,0,0,0.5); } - 移动端 WebView 中,遮罩层若没设
touch-action: manipulation,可能因延迟响应被系统吞掉点击
手写遮罩层时怎么防点击穿透和焦点逃逸
用 <div class="overlay"> 模拟 backdrop 时,DOM 结构和事件绑定稍有偏差,就会出现“点关闭按钮却关了遮罩”或“Tab 出弹窗”的问题。
关键控制点:
- 遮罩层和弹窗内容必须**平级兄弟关系**,不能把
<div class="modal">包在<div class="overlay">里——否则点击弹窗会冒泡到父遮罩,误触发关闭 - 遮罩层 click 监听必须判断目标:
overlay.addEventListener('click', e => { if (e.target === overlay) closeDialog(); });,不能直接绑overlay.onclick = closeDialog - 弹窗打开时,给
<body>加style="overflow: hidden",iOS Safari 特别吃这一条,否则背景照常滚动 - 禁用背景键盘交互:用
document.body.setAttribute('inert', '')(现代浏览器),或退化为aria-hidden="true"+ 手动移除所有tabindex属性
真正难的不是让弹窗出现,而是让它“存在得合理”:键盘能进、能循环、能出;屏幕阅读器能感知状态变化;移动端点得准、不误触;背景既不可见也不可交互。这些细节堆起来,才是可访问性的底线。



















