:focus-within比:focus更适合下拉搜索框显隐控制,因为它监听父容器内任意子元素(如输入框、补全项、清空按钮)是否获焦,而:focus仅作用于自身;需同容器包裹、子项设tabindex、避免display:none导致键盘导航失效,并配合aria-expanded等可访问性属性。

为什么:focus-within比:focus更适合下拉搜索框显隐控制
因为:focus只监听自身是否获焦,而:focus-within监听的是整个父容器内**任意子元素**是否获焦——包括输入框、补全项
常见错误是把和
- 写在不同
- 必须把
<input>和补全列表(如<ul class="suggestions"></ul>)包裹在同一个父容器内,比如<div class="search-wrapper"> <li>补全项需设<code>tabindex="-1"(推荐)或tabindex="0",否则键盘无法聚焦到它们,:focus-within状态会在焦点移出<input>时立即中断 - 父容器不能是
display: contents或visibility: hidden,否则浏览器无法建立焦点传播路径 - 别用
height: 0或display: none做初始隐藏——它们会让元素脱离渲染树和焦点流 -
max-height配合overflow-y: auto可滚动但不触发布局重排;Safari 对height动画极其敏感,容易丢焦点 - 如果补全项含链接,确保
<a></a>本身可聚焦(默认即可),或显式加tabindex="0" - 监听
focusin/focusout而非focus/blur,前者冒泡、更稳定 -
input必须有tabindex="0",否则键盘 Tab 无法进入,focusin不会触发 - CSS 动画改用
max-width+transform,避开width引起的布局重排 - 不要用
click事件切换状态——它覆盖不了键盘路径,也不响应屏幕阅读器的“虚拟光标”移动 - 给父容器加
role="combobox",并用aria-expanded="false"初始化;JS 在展开/收起时同步更新该值 -
aria-controls必须指向补全列表的id,例如aria-controls="search-suggestions",否则辅助技术无法关联控件与列表 - IE 全面不支持:focus-within;Edge 79+、Chrome 60+、Firefox 52+、Safari 15.4+ 支持良好;旧 Safari 需 fallback 到 JS +
focus/blur监听
tabindex="-1"却没同步处理焦点链,导致伪类完全不触发。
如何避免display: none导致键盘导航失效
用display: none隐藏下拉列表,再靠:focus-within切回display: block,会导致补全项彻底退出 tab 顺序,方向键选中、Enter 确认全部失灵。
正确做法是用视觉隐藏组合属性,既保持可访问性,又支持平滑过渡:
.search-wrapper .suggestions {
opacity: 0;
visibility: hidden;
pointer-events: none;
max-height: 200px;
overflow-y: auto;
}
.search-wrapper:focus-within .suggestions {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
移动端:focus-within为何常失效,怎么兜底
iOS Safari(尤其 15.4 之前)和部分 Android Chrome 对:focus-within的焦点追踪有延迟,软键盘弹出后<input>可能短暂失焦再重获焦,造成伪类快速切换、动画中断甚至闪退。
纯 CSS 方案在真机上不可靠,必须用 JS 显式管理状态:
const searchWrapper = document.querySelector('.search-wrapper');
const input = searchWrapper.querySelector('input');
input.addEventListener('focusin', () => {
searchWrapper.classList.add('is-expanded');
});
input.addEventListener('focusout', () => {
// 延迟移除,避开清空按钮点击竞态
setTimeout(() => {
if (!searchWrapper.matches(':focus-within')) {
searchWrapper.classList.remove('is-expanded');
}
}, 150);
});
无障碍与浏览器兼容性必须检查的三件事
:focus-within本身不解决可访问性,只是基础触发条件。真正让屏幕阅读器、键盘用户感知状态,还得靠语义化标记和动态属性同步。
最易被忽略的是:aria-expanded值必须真实反映当前视觉状态。哪怕 CSS 已显隐,若 JS 没同步更新这个属性,NVDA 或 VoiceOver 就会误报“列表已关闭”,用户根本不知道能选什么。











