element.focus()静默失败主因是元素不可聚焦、被display:none隐藏或DOM未就绪;showModal()不提供键盘焦点围栏,须手动聚焦首元素、拦截Tab循环、恢复原焦点;原生button自带可访问性,div+role="button"需手写全部交互逻辑。

调用 element.focus() 没反应,90% 是因为元素不可聚焦或 DOM 还没就绪;<dialog></dialog> 的 showModal() 不能替代手动焦点围栏;用 div + role="button" 而非原生 button,等于主动放弃默认可访问性保障。
为什么 focus() 总是静默失败?
不是 JS 写错了,而是浏览器根本拒绝聚焦——常见原因有三个:
- 元素没有可聚焦能力:只有
input、button、a[href]等原生语义元素默认可聚焦;div、span必须加tabindex="0"才能进 Tab 流 - 元素被
display: none隐藏:此时focus()直接静默失败(连错误都不抛);visibility: hidden虽不可见,但依然能聚焦,可能引发键盘用户困惑 - DOM 尚未完成渲染:动态插入(如
innerHTML、React 渲染后)立即调用focus(),常因样式计算/布局未完成而失效
实操建议:对非表单容器加 tabindex="0";检查是否误用了 display: none;动态插入后用 requestAnimationFrame(() => el.focus()) 替代 setTimeout(() => , 0)。
<dialog></dialog> 的 showModal() 为什么不能当焦点围栏用?
showModal() 在 Safari 全版本和旧 Edge 中不拦截 Tab 键,document.activeElement 可能停留在 body 上——它只管模态遮罩和 Esc 关闭,不管键盘导航。
立即学习“前端免费学习笔记(深入)”;
必须补三件事:
- 打开后立刻聚焦首个可聚焦项:
modal.querySelector('button, input, select, [tabindex="0"]') - 监听
keydown拦截Tab/Shift+Tab:仅当焦点在首/末元素时event.preventDefault(),再跳转到对端 - 关闭前缓存触发源(如存
data-trigger-id),关闭后校验该元素是否仍在 DOM 中(el.offsetParent !== null),再.focus()
别全局拦截所有 Tab 键——这会破坏 NVDA 等屏幕阅读器的内置导航(如 Insert+F7 列表)。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
为什么优先用原生 button 而不是 div + role="button"?
原生 button 自带焦点管理、空格/回车触发、disabled 语义、屏幕阅读器识别;div + role="button" 只是“告诉辅助技术它像按钮”,但不提供任何交互逻辑。
你得手写:
- 键盘事件(
Enter/Space触发) - 禁用状态处理(
aria-disabled="true"+ 样式 + 事件拦截) - 焦点样式与视觉反馈(
:focus-visible或自定义 class) - 焦点进入/离开时的状态同步(比如高亮父容器)
漏掉任意一项,键盘用户就卡住。这不是“多写几行代码”的问题,而是可访问性链路上的单点故障。
动态内容更新时,焦点该落到哪?
SPA 路由切换、AJAX 表单提交、实时数据刷新后,焦点不能留在原地或丢失——用户会迷失上下文。
策略取决于场景:
- 表单提交成功:聚焦到新内容区域的标题(如
h2),或用aria-live="polite"广播结果 - 错误反馈:聚焦第一个出错字段,并确保其
tabindex="0"(若原为tabindex="-1",需临时改) - 列表新增项:聚焦新插入的
li或其内第一个可聚焦子元素(如操作按钮) - 模态框关闭后:必须回到触发它的元素,且要验证该元素是否仍可聚焦(
offsetParent存在 +tabIndex >= 0)
最易忽略的是:焦点目标必须同时满足「可聚焦」+「可视」+「不在隐藏容器中」——三者缺一不可。


















