:focus能替代js控制下拉显隐,因其是原生焦点钩子,配合css(如opacity+pointer-events)和合理dom结构(可聚焦元素后紧跟下拉菜单并用+/~选择器关联),可实现自动展开/收起;但键盘导航、aria状态更新、移动端兼容等仍需js。

为什么 :focus 能替代 JS 控制下拉显隐
因为 :focus 是原生焦点状态钩子,配合 display 或 visibility 切换,就能让下拉菜单随输入框获得/失去焦点自动展开/收起——前提是 DOM 结构合理、可聚焦元素存在且语义正确。
关键不是“能不能”,而是“怎么组织结构才不卡住”:下拉列表必须紧跟在可聚焦元素(如 <input> 或 <button></button>)之后,且用相邻兄弟选择器 + 或通用兄弟选择器 ~ 关联。
-
<input type="text">必须设tabindex="0"或本身可聚焦(如没加disabled),否则无法触发:focus - 下拉容器不能用
display: none初始隐藏——它会让元素脱离流、无法被键盘Tab访问;推荐用opacity: 0; pointer-events: none;+ 过渡动画 - 若用
<select></select>原生控件,:focus无法控制其下拉面板,必须换为自定义结构
:focus 触发下拉时的 DOM 结构要求
浏览器只允许通过 CSS 选择器向上/向后找兄弟或后代,不能向前选父或前兄弟。所以输入框和下拉菜单必须是同级、且下拉在输入框之后。
<div class="filter-box">
<input type="text" aria-label="筛选关键词"><ul class="dropdown-menu">
<li>选项一</li>
<li>选项二</li>
</ul>
</div>
对应 CSS 必须写成:
.filter-box input:focus + .dropdown-menu {
opacity: 1;
pointer-events: all;
}
- 不能把
<ul></ul>放在<input>前面,否则+选不到 - 如果中间插了其他元素(比如提示文字
<span></span>),就得改用~(通用兄弟选择器),但要注意它会匹配所有后续同类元素 - 若需支持键盘操作(方向键选中),仍需 JS;纯
:focus只解决显隐,不处理交互逻辑
键盘焦点流转与可访问性陷阱
用户按 Tab 进入输入框 → 下拉展开 → 按 Tab 默认跳到下一个可聚焦元素,而不是下拉项。这是最大盲区:纯 CSS 无法劫持 Tab 流,也无法让 ↓ 键聚焦第一个 <li>。
- 给下拉里的
<li>加tabindex="-1",让它能被 JS 聚焦,但默认不可被Tab访问——这是可访问性标准做法,但需要 JS 配合 - 若坚持零 JS,至少要把下拉项做成
<a href="#"></a>或带tabindex="0"的<button></button>,确保键盘能抵达 -
aria-expanded和aria-controls属性必须手动写死在 HTML 里(如aria-expanded="false"),CSS 无法动态更新 ARIA 状态
移动端点击穿透与伪类失效问题
iOS Safari 和部分安卓 WebView 中,:focus 在点击非表单元素时可能不触发,或点击后立即失焦,导致下拉闪退。这不是 bug,是移动端对“焦点”的保守策略。
- 给
<input>加ontouchstart=""(空事件处理器)可强制唤醒焦点行为(兼容老版 iOS) - 更可靠的做法是同时监听
:focus和:hover:.filter-box input:focus + .dropdown-menu, .filter-box input:hover + .dropdown-menu——但注意:hover在触摸设备上可能残留 - Android Chrome 85+ 支持
:focus-visible,可用来区分鼠标/键盘焦点,避免触摸误触,但需降级 fallback
真正免 JS 的下拉筛选框,只适用于简单场景:用户点开看选项、点选项跳转或提交表单。一旦涉及高亮当前项、搜索过滤、键盘导航,就必须补 JS——:focus 只是开关,不是控制器。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











