:focus-within 能让父容器响应子元素聚焦并变色,但仅当子元素真实可聚焦且浏览器支持该伪类;有效触发元素包括 input、textarea、select、button、tabindex="0" 元素及带 href 的 a 标签,无效情况含 display:none、visibility:hidden 或 tabindex="-1" 未显式 focus()。

:focus-within 能直接让父容器响应子元素聚焦并变色,前提是子元素必须真实可聚焦且浏览器支持该伪类——不是所有“能点”的元素都能触发它。
哪些子元素能真正触发 :focus-within
只有当前获得键盘或鼠标焦点的元素,才会激活祖先的 :focus-within。常见有效元素包括:
-
<input>、<textarea></textarea>、<select></select>、<button></button>(原生可聚焦) - 显式设置
tabindex="0"的元素(如<div tabindex="0">) <li> <code><a href="#"></a>(有href时默认可聚焦) <div> 或 <code><p></p>即使加了tabindex="-1",但没被 JS 显式调用.focus(),也不会触发 —— 浏览器只认“此刻有没有焦点”,不认“能不能被聚焦”- 子元素被
display: none或visibility: hidden遮盖,焦点无法到达,:focus-within不会生效 - 不要写成
.parent:focus > .child—— 这是:focus作用于父自身,而父<div> 默认不可聚焦,永远不匹配 <li>不要混用 <code>:hover:focus-within—— 这表示“同时 hover 且子聚焦”,通常你需要的是“任一满足”,应写成.parent:hover, .parent:focus-within - 避免对深层嵌套结构写宽泛选择器,比如
.card:focus-within *,可能意外影响内部按钮、图标等已有样式 -
<input tabindex="-1">会导致键盘无法进入该字段,:focus-within也就不会被触发 -
focusin冒泡,能捕获子元素聚焦事件 -
focusout同样冒泡,可用于清理 class,避免残留 - 仅靠
focus事件绑定在父元素上,根本收不到子元素的聚焦信号
无效情况:
:focus-within 写法和典型错误
直接在父容器上写 :focus-within 即可,但容易踩这些坑:
兼容性和 fallback 方案
截至 2026 年 7 月,:focus-within 在 Chrome 60+、Firefox 61+、Edge 79+、Safari 15.4+ 均稳定支持;但 iOS 15.4 及更早版本 Safari 仍缺失,IE 完全不支持。
如需兼容老环境,JS fallback 必须监听 focusin 和 focusout(不是 focus/blur),因为:
示例 fallback:
const container = document.querySelector('.form-group');
container.addEventListener('focusin', () => container.classList.add('focused'));
container.addEventListener('focusout', () => container.classList.remove('focused'));
最常被忽略的点:不是“写了 :focus-within 就一定生效”,而是要确认焦点确实落到了一个合法的可聚焦子元素上——检查是否被 outline: none 遮蔽、是否被 pointer-events: none 拦截、或者父容器是否意外设置了 contain: layout 阻断了焦点传播。











