autofocus 属性仅在浏览器首次逐行解析 HTML 的那一刻生效,并且只作用于源码中第一个可聚焦的原生表单控件。它不会响应任何 JavaScript 操作,也不会在环境变化后重新触发。很多人误以为“运行环境初始化时自动聚焦”是一个可用的功能,实际上——并不存在。浏览器不会在 SPA 挂载、模态框弹出、路由切换或 SSR hydration 之后再次执行 autofocus。

autofocus 仅对原始 HTML 源码中首个可聚焦元素生效
这不是一个能够被 JavaScript 操控的生命周期钩子,而是浏览器在解析 HTML 字符串时的一次性行为,一旦错过便无法重来。具体表现如下:
- 写在源码里:
✅ 页面首次加载时有一定概率成功聚焦 - 用
el.setAttribute('autofocus', '')或input.autofocus = true❌ 完全无效果 - Vue/React 组件中写
→ SSR 渲染完成后 DOM 早已存在,autofocus 被跳过 - 模态框从
display: none切换为block→ 元素虽然存在,但初始解析早已完成,autofocus 不会再触发
哪些元素能真正响应 autofocus
只有原生可交互、能获得焦点的表单控件才可能有反应,而且还需要满足“未被禁用、未隐藏、首屏可见”这几个硬性条件。具体分类如下:
- ✅ 稳定支持:
、、、 - ⚠️ 行为不稳定:
(Firefox / 旧版 Safari 会忽略)、(Safari 有时跳过) - ❌ 完全无效:
、、、 - 如果元素被包裹在
visibility: hidden或display: none的容器中,autofocus会直接静默失效
移动端和 iOS Safari 中 autofocus 基本不可用
Apple 明确限制了非用户手势上下文中的自动聚焦行为,主要是为了防止软键盘在用户未操作时意外弹出。因此实际情况是:
- iOS Safari 在页面加载、从后台切回前台、iframe 内嵌等场景下,直接忽略
autofocus - 即便你在代码中写了
,iOS 用户打开页面后大概率没有任何反应 - Android Chrome 也越管越严,尤其是在 WebView 或 PWA 环境中
- 如果需要在模态框中聚焦,必须绑定到用户事件(比如
click、touchend)之后再调用.focus()
可靠替代:用 JS 手动 focus(),但必须带容错和时机判断
放弃纯 HTML 方案,改用命令式聚焦,同时避开几个常见陷阱:
- 先检查是否已经聚焦:
if (input && document.activeElement !== input),避免覆盖用户当前正在输入的位置 - SPA 场景下不要只依赖
useEffect(() => {}, []),应该监听路由变化(比如useLocation)或模态框显示事件(如shown) - SSR 环境要加保护:
if (typeof window !== 'undefined'),否则服务端会报document is not defined - 不要用
setTimeout(() => focus(), 0)—— 很容易和原生 autofocus 冲突,造成双聚焦闪烁 - 移动端 iOS 必须在用户手势回调内调用
.focus(),否则静默失败
真正容易被忽略的是:autofocus 的“自动”只存在于静态 HTML 加载的瞬间,一旦涉及任何动态行为——哪怕只是一个 CSS 动画过渡帧里出现了 opacity: 0——它就直接失效了。把它当作一个弱提示,而不是功能保障,才是更稳妥的做法。
