:focus-within 是css伪类,当父元素内任意子元素获得焦点时触发样式变化,适用于表单高亮、下拉菜单控制等场景,需确保子元素可聚焦且浏览器支持。

:focus-within 是什么,什么时候该用它
它让父元素在任意子元素获得焦点时触发样式变化,不用 JavaScript 就能实现“父级响应子级聚焦”的交互逻辑。适合表单容器高亮、下拉菜单展开控制、可访问性增强等场景。
常见错误是把它当 :focus 用——比如给 div 直接加 :focus-within 却忘了里面得有可聚焦子元素(input、button、带 tabindex 的元素),否则永远不生效。
- 必须确保子元素本身能获得焦点:原生表单控件、
button、或显式设置tabindex="0"的元素 - 不能用于 SVG 元素内部(除非 SVG 子元素明确支持焦点,如
<a></a>或带tabindex的<g></g>) - IE 完全不支持,Edge 79+ 和所有现代 Chrome/Firefox/Safari 都 OK
怎么写一个可用的:focus-within 表单高亮效果
目标:用户点进输入框时,整个 .form-group 加上边框和背景微调,提升视觉反馈。
关键不是“加样式”,而是确保结构和可访问性同时成立:
立即学习“前端免费学习笔记(深入)”;
- 父容器用
div.form-group,别用span或无语义标签(它们默认不可聚焦,但:focus-within不依赖父自身聚焦) - 子
input必须没有tabindex="-1",否则键盘无法到达,:focus-within也不会触发 - 避免在
:focus-within里改display或触发布局重排,可能引起焦点丢失(尤其 Safari)
示例 CSS:
form .form-group {
padding: 8px;
border-radius: 4px;
}
form .form-group:focus-within {
background-color: #f8f9fa;
border: 2px solid #007bff;
}
为什么:focus-within 比 JS 监听 focus/blur 更稳
它由浏览器原生处理,不依赖事件绑定顺序、不担心事件冒泡被阻止、也不用手动管理 focus/blur 配对(比如子元素 blur 后父容器要不要退样式?JS 很容易漏掉)。
但要注意几个隐性限制:
- 子元素获得焦点后,如果父容器被隐藏(
display: none),焦点会丢失,且再次显示时不会自动恢复——这不是:focus-within的问题,而是 DOM 焦点机制决定的 - 使用
contenteditable元素时,某些浏览器(旧版 Firefox)可能不触发:focus-within,需降级监听focusin - 和
:has()组合使用要小心:目前:focus-within可以安全用在:has()内部(如div:has(input:focus-within)),但反过来不行
移动端和可访问性要注意什么
移动端软键盘弹出时,input 获得焦点,:focus-within 正常触发;但部分安卓 WebView(尤其是 Android 9 以下)对 :focus-within 支持不稳定,表现为样式延迟或不触发。
更关键的是可访问性链路:
- 屏幕阅读器用户按 Tab 键进入
input,父容器样式变化是纯视觉反馈,不会自动朗读——需要额外加aria-live或aria-describedby来补充语义 - 如果父容器本身有
tabindex="0",它就能被 Tab 到,此时:focus-within和:focus可能同时生效,样式冲突需用 specificity 控制 - 不要仅靠
:focus-within实现核心功能(比如“只有聚焦时才显示提交按钮”),那会让键盘用户卡住——它只是增强,不是控制流
真正难的不是写对选择器,而是想清楚:这个“父感知子焦点”的行为,在键盘导航、屏幕阅读、动画中断、DOM 动态插入各种情况下是否依然可靠。很多 bug 出现在子元素是 JS 动态渲染出来的,却忘了它初始状态不可聚焦。










