模态框焦点锁定原理是监听focusin事件,用modalEl.contains(document.activeElement)判断焦点是否在内部,若跳出则阻止默认行为并聚焦首个可聚焦元素;tabindex="-1"仅支持JS聚焦,aria-modal="true"仅作语义提示,不影响原生Tab行为。
模态框内 Tab 键不跳出容器的原理
浏览器默认会把
键在所有可聚焦元素间线性遍历,包括模态框外的按钮、链接甚至
。模态框要“锁住”焦点,本质是拦截焦点离开边界的行为,并主动把焦点跳回第一个或最后一个可聚焦子元素。
关键不是阻止
,而是监听
事件,判断目标是否还在模态框内部;一旦发现焦点即将落到外部,立刻
并手动
回去。
用
+
判断焦点位置
这是最轻量、兼容性最好的判断方式:拿到当前获得焦点的元素,看它是不是模态框 DOM 节点的后代。
返回
表示焦点仍在框内
注意:如果用户用鼠标点击了模态框外区域,
可能是
或
等,必须覆盖所有情况
不要只监听
—— 用户可能用 Shift+Tab、鼠标点击、屏幕阅读器切换,
是唯一能捕获所有入口的事件
和
的实际作用
只让元素可通过 JS
聚焦,但不会进入自然 Tab 顺序;它常用于模态框自身(避免被 Tab 到背景遮罩层),或临时聚焦某个非交互元素(比如标题)。
立即学习
“
前端免费学习笔记(深入)
”;
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
是给辅助技术用的语义声明,告诉屏幕阅读器“当前只应操作这个模态框”,但它**完全不影响原生 Tab 行为** —— 浏览器照样会把焦点移出去。
别指望
替你做焦点管理,它只是辅助提示
模态框根节点加
是好习惯,方便 JS 初始化时
进去
遮罩层(overlay)建议设
并
,否则可能被意外聚焦
循环焦点时第一个/最后一个元素怎么选
“循环”不是指按 Tab 就从末尾跳回开头,而是当焦点试图离开末尾时,跳回第一个;Shift+Tab 试图离开开头时,跳回末尾。关键在于识别两个边界点。
第一个可聚焦元素:通常用
找
最后一个:用
拿数组后取
,别用
—— 它可能是文本节点或不可聚焦容器
动态内容(比如异步加载表单)需重新计算可聚焦元素列表,不能只初始化一次
如果模态框里没任何可聚焦元素,
会失败,最好 fallback 到模态框自身(已设
)
焦点管理真正麻烦的从来不是“怎么跳”,而是“什么时候该跳”——比如弹窗嵌套、动态渲染、第三方组件插入 DOM 后自动聚焦。这些场景下,靠一次性初始化远远不够,得监听 DOM 变化或重置焦点逻辑。
TabbodyTabfocusinevent.preventDefault()focus()document.activeElementcontains()modalEl.contains(document.activeElement)trueactiveElementbodyinputkeydownfocusinmodalEl.addEventListener('focusin', (e) => {
if (!modalEl.contains(e.target)) {
const focusable = modalEl.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (focusable.length > 0) {
focusable[0].focus();
}
}
});tabindex="-1"aria-modal="true"tabindex="-1".focus()aria-modal="true"aria-modaltabindex="-1".focus()tabindex="-1"aria-hidden="true"querySelectorbutton, [href], input:not([type="hidden"]), [tabindex]:not([tabindex="-1"])querySelectorAll[length - 1]lastElementChildfocus()tabindex="-1"