使用 role="dialog" 配合 aria-modal="true" 是实现语义化、可访问模态框的关键组合,但二者不自动锁死焦点,需手动 JavaScript 实现焦点陷阱,并配合 aria-labelledby、tabindex="-1" 及 CSS 隔离确保完整可访问性。

使用 role="dialog" 配合 aria-modal="true" 是实现语义化、可访问模态框的关键组合,但仅靠这两个属性本身**不会自动锁死焦点**。焦点锁死(focus trapping)需要手动 JavaScript 实现,而 aria-modal="true" 的作用是告诉屏幕阅读器:当前模态框是模态的,背景内容应被忽略(即“屏蔽”语义),同时触发部分浏览器(如 Chrome、Edge 85+、Firefox 76+)的**原生焦点限制行为**——但这仍不完全可靠,尤其在旧版浏览器或复杂 DOM 结构中。
role="dialog" 与 aria-modal="true" 的正确用法
二者需配合使用,并确保模态框有明确的标题(通过 aria-labelledby 指向一个可见或 visually-hidden 的标题元素):
-
role="dialog"告诉辅助技术这是一个对话框,影响其朗读方式和交互逻辑 -
aria-modal="true"是关键开关:启用后,主流屏幕阅读器(NVDA、JAWS、VoiceOver)会自动忽略模态框外的可聚焦元素 - 必须设置
aria-labelledby(指向标题 ID)或aria-label,否则会被视为无效 dialog - 模态框容器需有
tabindex="-1",以便程序化聚焦(首次打开时聚焦到容器本身)
焦点锁死不能只靠 ARIA 属性
ARIA 规范不定义焦点管理行为,aria-modal="true" 仅影响屏幕阅读器的“感知范围”,不控制键盘焦点流向。实际中仍可能出现:
- 按 Tab 键焦点逃逸到背景元素(尤其在 Safari 或未完全支持的浏览器中)
- Shift+Tab 跳出模态框顶部
- 点击背景或外部区域未阻止焦点切换
因此,必须补充 JavaScript 焦点陷阱逻辑。
立即学习“前端免费学习笔记(深入)”;
手动实现焦点锁死的核心逻辑
监听 keydown(Tab 键)和 focusin 事件,主动将焦点约束在模态框内所有可聚焦元素上:
- 收集模态框内所有可聚焦元素:
modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])') - 记录第一个和最后一个可聚焦元素
- Tab 时:若焦点在最后一个元素且按 Tab → 阻止默认行为,聚焦第一个;若在第一个且按 Shift+Tab → 聚焦最后一个
- 监听
focusin:若焦点移入模态框外 → 立即element.focus()回到最后一个有效焦点位置 - 模态框关闭时,记得恢复原焦点(通常保存在打开前)
推荐实践:结合 ARIA + JS + CSS
完整可访问模态框应包含三部分:
- HTML 结构:正确 role、aria-modal、aria-labelledby、tabindex="-1"
- JavaScript:焦点陷阱 + Esc 关闭 + 点击遮罩关闭 + 焦点返回
-
CSS:用
inert属性(现代浏览器支持)或pointer-events: none+aria-hidden="true"隐藏背景交互(注意inert尚未全兼容,需降级处理)
不依赖单一机制,而是分层保障:ARIA 提供语义与辅助技术支持,JS 保证键盘行为,CSS 辅助视觉与指针隔离。



















