防止焦点在模态框外泄露的关键是正确建立并维护可控焦点边界,需四步闭环:打开时立即聚焦首个可聚焦元素;Tab键循环限制在模态框内;背景内容设inert或polyfill禁用交互;关闭后将焦点返回触发源或最近可聚焦祖先。
防止焦点在模态框外泄露,关键不是“避免陷阱”,而是**正确建立并维护一个可控的焦点边界**。焦点逃逸本质是边界没封住、初始没捕获、退出没归还——三者缺一不可。
模态框显示后,必须在 DOM 渲染完成的下一帧(如用 setTimeout(..., 0) 或 requestAnimationFrame)将焦点设到第一个可聚焦元素上。不能依赖 autofocus 属性,它不可靠且可能被浏览器忽略或与其他组件冲突。
)或首个输入框,确保语义清晰、位置合理getTabables() 工具函数辅助)仅靠初始聚焦不够。用户按 Tab 键时,焦点必须在模态框内循环,不能流到背景按钮、导航栏或页脚。
Tab 键(区分 shiftKey)a[href], button:enabled, input:enabled, textarea:enabled, select:enabled, [tabindex]:not([tabindex="-1"])
光锁住焦点还不够——背景元素仍可能被点击、被屏幕阅读器读取,甚至意外触发交互。最稳妥的方式是让它们“不可聚焦、不可交互”。
或主内容区)添加 inert 属性,浏览器原生支持该语义(Chrome 105+、Firefox 122+、Safari 18.1+)tabindex="-1" + aria-hidden="true" + 禁用 pointer-eventsvisibility: hidden 或 display: none,它们不阻止键盘聚焦
模态框关闭瞬间,焦点若落在 document.body 或丢失,用户会迷失位置,尤其对屏幕阅读器用户极不友好。
const trigger = document.activeElement,或显式传入触发按钮引用trigger.focus();若触发源已销毁(如按钮被移除),则退回到逻辑上最近的可聚焦祖先(如导航菜单项、页面标题等)不复杂但容易忽略——真正起作用的,是这四步闭环:进得来、转得开、出不去、回得去。