aria-modal="true" 不自动创建焦点屏障,仅向辅助技术声明模态框为唯一可交互区域;必须手动实现焦点陷阱,通过 javascript 拦截 tab 键并循环聚焦首尾可聚焦元素,同时确保模态框具有 role 属性、是 body 直接子节点,并配合 aria-hidden、esc 关闭、初始焦点设置等完整逻辑。

aria-modal="true" 本身不创建焦点屏障
很多人以为只要加了 aria-modal="true",浏览器就会自动把键盘焦点锁在模态框里——实际不会。aria-modal 只是向辅助技术(如屏幕阅读器)声明“当前模态框是唯一可交互区域”,但浏览器原生不拦截 Tab 键切换,焦点仍会逃逸到背景元素。
必须手动实现焦点陷阱(focus trap)
真正的焦点屏障靠 JavaScript 主动控制:监听 Tab 键,检测焦点即将离开模态框时,强制将其循环回第一个或最后一个可聚焦子元素。
- 获取模态框内所有可聚焦元素:
modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])') - 记录首尾元素:
const first = focusable[0],const last = focusable[focusable.length - 1] - 在
keydown事件中判断:if (e.key === 'Tab'),再根据document.activeElement位置决定是否e.preventDefault()并first.focus()或last.focus()
配合 aria-modal 的必要 DOM 结构
aria-modal="true" 要生效,必须满足两个前提:
- 模态框元素需有
role="dialog"或role="alertdialog" - 模态框必须是
body直接子节点(不能嵌套在其他div下),否则部分屏幕阅读器无法正确识别上下文 - 背景内容建议加
aria-hidden="true"(注意:仅对辅助技术隐藏,视觉上仍需用 CSS 遮罩)
别漏掉 Esc 关闭和初始焦点设置
焦点陷阱只是其中一环。完整体验还需:
- 按
Esc时关闭模态框,并将焦点还原到触发按钮(用data-previous-focus记录) - 模态框打开后,立即
first.focus()—— 否则用户按Tab前焦点还在页面任意处 - 关闭后恢复
body滚动(如果之前禁用了),并清除aria-hidden和aria-modal
最易被忽略的是:焦点陷阱逻辑必须在模态框 display: block 且已渲染到 DOM 后才绑定,否则 querySelectorAll 找不到元素。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











