:focus-within能让搜索框“点开就展开”,因其监听父容器内任意子元素获焦状态,需满足三条件:父容器包裹可聚焦子元素、子元素真实可获焦、浏览器支持(ie不支持)。

直接用 :focus-within 就能实现点击/Tab 进入即展开,无需 JS,但必须满足三个硬性条件:父容器包裹可聚焦子元素、子元素真实可获焦、浏览器支持(IE 不行)。
为什么 :focus-within 能让搜索框“点开就展开”
它不是监听输入框自身焦点,而是监听“父容器内任意子元素是否获得焦点”。只要把 <input> 和下拉列表(比如 <ul class="suggestions"></ul>)包在同一个 <div class="search-container"> 里,用户点输入框、按 Tab 进去、甚至键盘方向键选中下拉项,整个容器都保持 <code>:focus-within 状态——自然触发展开逻辑。
常见错误是只给 <input> 写 :focus,结果父容器宽度/下拉显隐完全没反应;或者把输入框和下拉列表分开放在不同 DOM 节点下,:focus-within 根本不生效。
:focus-within 展开下拉时别用 display: none
用 display: none 隐藏下拉列表,再靠 :focus-within 切成 display: block,会导致键盘用户无法用方向键操作补全项——因为 display: none 元素彻底脱离渲染树和 tab 顺序。
正确做法是用组合属性控制“视觉隐藏但保留可访问性”:
-
opacity: 0; visibility: hidden; pointer-events: none;作为初始隐藏状态 .search-container:focus-within .suggestions { opacity: 1; visibility: visible; pointer-events: auto; }- 如果下拉列表需要滚动,加
max-height: 200px; overflow-y: auto;,但别设height: 0或触发布局重排,Safari 容易因此丢失焦点状态
移动端 Safari 和旧版 Edge 的兼容性坑
iOS Safari 15.4 之前、Edge 79 之前都不支持 :focus-within;即使新版 Safari,在某些场景下点击输入框也不会触发,因为它对“焦点获取”的判定更严格——仅靠 tabindex="0" 不够,得确保子元素是原生可聚焦控件(如 <input>、<button></button>)。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
必须检查的点:
- 父容器不能是
<span></span>或无语义<div>,虽然它本身不用聚焦,但结构需语义清晰 <li> <code><input>不能有tabindex="-1",否则键盘用户根本进不去 - Chrome 中浏览器自动填充(autofill)可能覆盖自定义下拉样式,建议设
autocomplete="new-password"或带时间戳的随机值(如autocomplete="search-1712345678") - 屏幕阅读器需要
role="combobox"+aria-expanded+aria-controls,这些仍需少量 JS 同步更新,纯 CSS 搞不定
动画和过渡要避开布局抖动
想加展开动画?优先用 opacity、transform、max-height,别碰 width、height、display 这类触发布局计算的属性。
例如搜索框拉宽效果可以这样写:
.search-container {
width: 200px;
transition: width 0.3s ease;
}
.search-container:focus-within {
width: 300px;
}
但注意:如果容器内有浮动元素或 flex 子项,width 变化可能引起 reflow,导致焦点意外丢失(尤其 Safari)。更稳的方式是用 transform: scaleX(1.5) 配合 transform-origin: left。
真正容易被忽略的是:所有交互状态必须和辅助技术同步。比如 aria-expanded="false" 初始值必须真实,且在 :focus-within 生效时由 JS 改为 "true"——否则屏幕阅读器读不出当前是否已展开。










