autofocus在spa路由跳转后不会触发,因浏览器仅在初始html解析时执行一次;必须用focus()替代,且需确保元素已挂载、可见、可聚焦,并在ios safari中绑定用户手势调用。

autofocus在SPA路由跳转后根本不会触发
这不是 bug,是浏览器规范行为:autofocus 只在初始 HTML 解析时扫描并执行一次,后续所有 JS 驱动的 DOM 变更(包括 React Router 跳转、Vue Router 切换、history.pushState)都不会重新评估该属性。你看到“新页面输入框没聚焦”,是因为浏览器压根没看第二眼。
必须用 focus() 替代,但时机很关键
手动调用 input.focus() 是唯一可靠路径,但直接在组件挂载回调里写往往失败——尤其在移动端或 CSS 动画未结束时。需要同时满足三个条件:
- 元素已挂载:
input.isConnected === true - 元素可见:
getComputedStyle(input).display !== 'none' && getComputedStyle(input).visibility !== 'hidden' - 元素可聚焦:
input.tabIndex >= 0 && !input.disabled && !input.hasAttribute('inert')
推荐统一用 requestAnimationFrame(() => input.focus()) 延迟一帧,避开渲染未完成或 transition 中的不可见状态。
iOS Safari 下 focus() 必须绑定用户手势
这是最容易被忽略的硬限制:iOS Safari 会静默拒绝任何非用户直接触发的 focus() 调用。单纯靠 useEffect 或 onMounted 几乎必然失败。
- 模态框场景:在打开按钮的
onClick/ontouchend回调里立即调用input.focus() - 搜索页/登录页等“自动”场景:加一层
setTimeout(() => input.focus(), 300)提升成功率(虽不 100%,但比无延迟强) - 绝对避免:在路由
afterEach钩子或popstate监听中直接调用focus(),除非你能确认该事件源自用户点击(比如通过 event.isTrusted 判断)
多个 autofocus 元素共存会导致焦点错乱
HTML 规范只允许首个被解析到的 autofocus 元素生效。如果初始页面已有带 autofocus 的搜索框,而新路由组件里又写了另一个,后者永远抢不到焦点——哪怕它视觉上更相关。
解决办法很简单:删掉所有模板里的 autofocus 属性,统一收口到 JS 控制,并加保护逻辑:
if (input && document.activeElement !== input) {
input.focus({ preventScroll: true });
}
这个 { preventScroll: true } 很重要,能避免聚焦时页面意外滚动;而原生 autofocus 不支持该选项。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











