动态插入的 input 无法 focus 是因浏览器拒绝聚焦未就绪元素,需满足四个条件:元素已渲染且可见、在文档流中、调用由用户手势触发、未被禁用或设 tabindex="-1";推荐用 requestAnimationFrame 聚焦并检查 activeElement 和 offsetParent。

动态插入的 input 为什么 focus() 没反应
不是 JS 写错了,而是浏览器拒绝聚焦一个“还没准备好”的元素。DOM 插入完成 ≠ 渲染完成 ≠ 焦点系统就绪。常见于 innerHTML、insertAdjacentHTML、Vue 的 v-if 切换、React 的 useState 触发重渲染后。
实操建议:
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 别用
setTimeout(() => input.focus(), 0)—— 它不保证渲染完成,比requestAnimationFrame更不可靠 - 统一用
requestAnimationFrame(() => input.focus()),它在下一次绘制前执行,能等 DOM 布局就绪 - 若元素在 Shadow DOM 中,先确认
shadowRoot已挂载,再查子节点并聚焦 - 检查
input是否被display: none或visibility: hidden包裹:getComputedStyle(input).display !== 'none'必须为真
focus() 调用前必须检查的四个硬性条件
浏览器对 focus() 静默失败,不报错也不提示。你只能靠排查。漏掉任一条件,光写 input.focus() 就是白调。
实操建议:
立即学习“前端免费学习笔记(深入)”;
-
document.activeElement !== input:先判断是否已是焦点,避免无效调用 -
input.offsetParent !== null:确保元素在文档流中(没被移除、没被display: none掩盖) - 调用上下文必须是用户手势触发(如
click、keydown回调),否则 iOS Safari 和部分 WebView 会拦截 - 元素不能被
disabled,也不能是tabindex="-1"(它只支持程序聚焦,不参与 Tab 流;需可导航请用tabindex="0")
模态框打开后焦点没进内容区
这不是“没聚焦”,而是焦点落在了模态框容器本身(比如 <dialog> 或 <div role="dialog">),而它的子元素又没被正确识别为可聚焦项。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 不要依赖
autofocus属性——它只在 HTML 初始解析时生效,对动态插入的元素完全无效 - 在模态框显示完成后再聚焦:原生
<dialog>用shown事件;Bootstrap 用shown.bs.modal;React 用useEffect配合 ref;Vue 用onMounted+nextTick - 聚焦目标要明确:
modal.querySelector('button, input, select, textarea, [tabindex="0"]'),避开[tabindex="-1"] - 加
{ preventScroll: true }参数防止软键盘弹出时页面滚动:input.focus({ preventScroll: true })(注意 Safari 15.4+ 才支持)
Tab 键跳出模态框或滚动容器
焦点“消失”不是丢了,是容器本身没资格进 Tab 流。比如 <div class="card-list"> 滚动区域没设 tabindex="0",Tab 到末尾就跳到页面底部;模态框没做焦点围栏,Tab 就直接穿出去。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 滚动容器必须设
tabindex="0",否则无法自动滚动到新焦点项 - 监听模态框的
focusin事件(它冒泡,focus不冒泡),检查!modal.contains(event.target),不满足就立刻firstFocusable.focus() - Tab 循环不能只靠
keydown拦截所有Tab—— 屏幕阅读器快捷键不会触发 keydown,但会改变焦点。必须配合focusin捕获所有入口 - 每次内容更新(如 AJAX 加载、框架 re-render)后,必须重新运行
modal.querySelectorAll('button, input, select, textarea, [tabindex]:not([tabindex="-1"])'),旧列表会过期
body 上导致下一次 Tab 断链——这些都不是单次 focus() 能解决的,得靠状态锁、容器级事件委托和可聚焦元素实时快照。


















