:focus-within在移动端展开动画大概率失效,因Safari和部分Android浏览器对其支持不稳定,软键盘弹出易导致焦点丢失;可靠方案是JS显式管理is-expanded类,配合focusin/focusout、max-width动画及无障碍属性。

直接用 :focus-within 在移动端做搜索框展开动画,大概率失效——不是写法错,而是 Safari 和部分 Android 浏览器对 :focus-within + width 动画的支持不稳定,且 :focus 本身在软键盘弹出后常丢失焦点状态。
为什么:focus-within在移动端展开时会闪退或不触发
iOS Safari(尤其 15.4 之前)对 :focus-within 的焦点链追踪有延迟;软键盘弹起后,input 可能短暂失焦再重获焦,导致伪类快速切换、动画中断。Android Chrome 某些版本对点击放大镜图标后调用 input.focus() 的响应也不一致。
- 点击图标 → JS 调用
input.focus()→ 焦点未真正落在input上,:focus-within不生效 - 用户快速点击清空按钮或切换 Tab,
blur事件比动画完成早,容器提前退出:focus-within状态 - 父容器用了
overflow: hidden或固定height,动画过程中布局重排,Safari 直接丢弃焦点状态
必须用 JS 控制 is-expanded 类名,而不是依赖纯 CSS
真机环境下,唯一可靠的方式是用 JS 显式管理状态:监听 focusin 和 focusout(比 focus/blur 更稳),加防抖和延迟收起逻辑。
-
focusin触发时立即加is-expanded类,确保动画同步启动 -
focusout后用setTimeout(() => el.classList.remove('is-expanded'), 150),避开清空按钮点击的竞态 -
input必须有tabindex="0",否则键盘 Tab 进入时无法触发focusin - 不要监听
click切换类名——它无法覆盖键盘聚焦路径
CSS 动画必须避开 width,改用 max-width + transform
width: 0 或 width: 32px → 240px 在移动端会因布局重排导致焦点丢失;max-width 不改变盒模型,更安全。
立即学习“前端免费学习笔记(深入)”;
- 初始状态设
max-width: 40px(包住图标+留白),不是0 - 展开时用
max-width: min(320px, 80vw),适配不同屏幕 - 搭配
overflow: hidden和transition: max-width 0.3s ease-in-out - 如需更高性能,给容器加
will-change: transform或用transform: scaleX(1)替代宽度变化
下拉建议列表不能用 display: none 切换
键盘用户按方向键选中补全项时,如果下拉区靠 display: none → block 切换,元素会彻底脱离 tab 顺序,无法聚焦。
- 初始隐藏用
opacity: 0; visibility: hidden; pointer-events: none; -
.search-container.is-expanded .suggestions中恢复为opacity: 1; visibility: visible; pointer-events: auto; - 滚动区域加
max-height: 200px; overflow-y: auto;,别设height: 0—— Safari 对此极其敏感 - 补全列表必须和
input在同一父容器内,且该容器要有role="combobox"和aria-expanded属性同步
真正容易被忽略的是:很多人写了 :focus-within 却没检查 input 是否真实可聚焦——比如被 pointer-events: none 拦住,或父层用了 display: contents,浏览器根本无法确认焦点落点,伪类自然不触发。


















