:focus-within 是css伪类,当元素自身或其任意后代获得焦点时触发;它比:focus更适合搜索栏展开,因能直接作用于父容器实现点击即展开,天然支持鼠标和键盘聚焦且零js。

focus-within 是什么,为什么它比 focus 更适合搜索栏展开
:focus-within 是 CSS 伪类,当元素自身或其任意后代获得焦点时触发。对搜索栏来说,输入框 <input> 获焦时,它的父容器(比如 <div class="search">)就能响应样式变化——这正是“点击即展开”的核心机制。
<p>用纯 <code>:focus 只能作用于 <input> 自身,没法拉长外层容器;而 JS 监听 focus 事件再切 class 又多一层逻辑、易漏掉键盘聚焦(如 Tab 进入)。:focus-within 天然覆盖鼠标点击 + 键盘聚焦,且零 JS。
基础写法:给容器加 focus-within,控制 width 或 transform
关键不是改 input,而是改它的直接父容器(或更高层封装容器),否则宽度撑不开、动画不连贯。
- 确保
<input>是目标容器的**后代**,不是兄弟或隔代 - 初始状态设为收缩(如
width: 40px),:focus-within下设为展开(如width: 240px) - 必须加
transition,否则宽度突变;推荐用width+min-width组合,避免 layout shift
示例:
立即学习“前端免费学习笔记(深入)”;
.search {
width: 40px;
min-width: 40px;
transition: width 0.2s ease;
}
.search:focus-within {
width: 240px;
}
常见失效原因:HTML 结构和 tabindex 陷阱
很多情况写了 :focus-within 却没反应,基本是以下三点之一:
-
<input>被包在<label></label>里但用了for/id关联,而 input 没有tabindex—— 点击 label 不会让 input 获焦,:focus-within不触发 - 容器设置了
overflow: hidden,展开后内容被裁剪(尤其带 icon 的搜索栏) - input 被设了
display: none或visibility: hidden—— 隐藏元素无法获焦,:focus-within永远不生效
修复建议:用 <label><input></label> 嵌套结构,或确保 input 有 tabindex="0"(仅当它默认不可聚焦时)。
兼容性与 fallback:Safari 15.4+ 才完全支持 focus-within 动画
旧版 Safari(≤15.3)和 IE 完全不支持 :focus-within;即使新版 Safari,对 width 的 transition 在 flex 容器中也可能卡顿。
- 用
@supports (selector(:focus-within))包裹样式,避免干扰老浏览器 - 更稳妥的展开方式是用
transform: scaleX()或max-width,比直接改width兼容性更好 - 如果项目需支持 Safari 14 或更低,就得回退到 JS:监听
focusin和focusout,切换 class
真正容易被忽略的是:focus-within 的“后代”判定只看 DOM 树,不看视觉层级。如果用了 position: absolute 把 input 移出容器,哪怕视觉上还在里面,:focus-within 也失效。










