autofocus仅在HTML初始解析时生效,异步插入的元素需手动focus()并确保已挂载、可见、可聚焦;iOS Safari要求focus()必须在用户手势同步上下文中调用;应避免干扰用户当前操作。

autofocus在异步组件里根本不会触发
它只在浏览器解析原始 HTML 字符串时扫描一次,Vue/React 动态挂载、innerHTML 插入、fetch 后 appendChild 的元素,哪怕带 autofocus 属性,浏览器也视而不见。这不是 bug,是规范行为——DOM 构建完成后,autofocus 就“过期”了。
常见现象包括:<input autofocus> 写在 Vue <template> 里,组件挂载后光标没动;模态框用 fetch 加载表单片段再插入,输入框始终不聚焦;服务端返回带 autofocus 的 HTML 片段,客户端插入后焦点却落在页面顶部旧输入框上(因为它是第一个被初始解析的)。
手动 focus() 必须等三件事同时满足
不能一挂载就调 input.focus(),得确保:元素已挂载、可见、可聚焦。否则要么报错,要么静默失败。
- 检查挂载:
input.offsetParent !== null或input.isConnected - 检查可见:
getComputedStyle(input).display !== 'none'且getComputedStyle(input).visibility !== 'hidden' - 检查可聚焦:
input.tabIndex >= 0且未被disabled或inert阻断 - 推荐延迟一帧:
requestAnimationFrame(() => input.focus()),避开 CSS transition 未结束或渲染未完成的时机
iOS Safari 对 focus() 有手势限制
它要求 focus() 必须发生在用户手势(click、tap、keydown)触发的同步上下文中,否则直接忽略。这意味着:
立即学习“前端免费学习笔记(深入)”;
- 单纯靠
useEffect(() => ref.current?.focus(), [])或onMounted(() => inputRef.value?.focus())在 iOS 上大概率白忙活 - 模态框打开若由按钮点击触发,应在该点击事件回调里立即调用
input.focus(),而不是等组件挂载后再执行 - SPA 路由跳转后想聚焦,需监听
popstate或框架路由事件,并确认当前操作源自用户导航(如history.pushState是用户点击触发的)
多个 autofocus 元素会引发不可控竞争
HTML 规范只要求浏览器对第一个被解析到的 autofocus 元素生效,其余全部静默忽略。问题在于:“第一个”取决于源码顺序,而非渲染顺序或 CSS 显示状态。更危险的是:
- 如果初始 HTML 里有个隐藏的搜索框写了
autofocus,而异步加载的登录表单里也有一个,后者永远抢不到焦点 - 服务端渲染后客户端 hydrate,两个
autofocus可能同时存在,但只有 SSR 输出中那个生效,JS 渲染的那个无效 - 解决办法很简单:删掉所有
autofocus属性,统一用 JS 控制,且调用前加保护逻辑:if (input && document.activeElement !== input) input.focus()



















