:focus-within必须写在父容器上才有效,因其专为“监听者”设计,用于让父级感知后代焦点;若加在input等子元素自身则永不触发,正确做法是将其应用于包裹可聚焦子元素(如input、textarea)的div、label等父容器,并确保子元素真正可聚焦且焦点链未被display:contents等样式阻断。

:focus-within 必须写在父容器上才有效
很多人写了 :focus-within 没反应,是因为加在了 <input> 自身上。它不是 :focus 的替代品,而是专为“监听者”设计的伪类:只有包裹 <input>、<textarea> 或带 tabindex 的元素的父级(比如 <div class="form-group"> 或 <label>)加上它,才能响应内部任意子元素的焦点状态。
常见错误写法:<input class="input" /> 上写 .input:focus-within —— 永远不触发。正确做法是把伪类写在它的直接或间接父容器上。
-
<label>和<input>必须构成祖先-后代关系(<label><input></label>或<label for="x"></label><input id="x">),否则点击<label>不会触发:focus-within - React/Vue 中的自定义组件(如
<MyInput>)需确保原生<input>是可聚焦的,且没被tabindex="-1"或pointer-events: none拦住 - 若用
display: contents或visibility: hidden包裹子元素,浏览器可能无法确认焦点落点,导致伪类失效
父容器必须能参与焦点流
:focus-within 不要求父容器本身获得焦点,但它必须处于“可聚焦上下文”中——也就是说,要么是原生可聚焦元素(如 <form>、<fieldset>),要么显式声明 tabindex="-1"(仅需一次,不干扰 tab 顺序)。
纯 <div> 默认不可聚焦,直接套 :focus-within 就是无效的。这不是 bug,是规范行为。
立即学习“前端免费学习笔记(深入)”;
- ✅ 推荐写法:
<div class="form-group" tabindex="-1">...</div> - ❌ 错误写法:
<div class="form-group">...</div>(没任何聚焦能力) - 注意:不要给整个
<form>加:focus-within,否则点任意按钮也会触发,容易误高亮
高亮样式别改布局属性
视觉反馈要明显,但不能引发重排或跳动。很多翻车案例都来自在 :focus-within 规则里改 height、margin 或 display。
尤其在表格中,<tr> 高度变化会导致整行甚至整表重绘;嵌套结构里改 padding 可能撑开容器,覆盖文字或错位边框。
- 优先用
outline: 2px solid #007bff:不占空间、无重排、键盘导航友好 - 要用阴影,选
box-shadow: 0 0 0 2px rgba(0,123,255,0.25),别加inset,否则往里缩盖住内容 - 统一所有相关元素的
box-sizing: border-box,避免border+padding导致宽度溢出 - 避免同时用
outline和border,容易视觉偏移或重叠
兼容性与调试要点
Chrome 65+、Firefox 61+、Safari 15.4+ 原生支持,IE 完全不支持。旧版 Safari(focusin。
鼠标点击不触发?键盘 Tab 正常但鼠标点不了?先看 document.activeElement 返回值——如果不是预期的 <input>,说明焦点被封装组件劫持或未透出。
- Shadow DOM 中使用时,
:focus-within必须写在 shadow host 上,且子元素焦点能穿透出来 - 某些 UI 库(如老版本 Ant Design)会把真实
<input>隐藏并代理 focus,此时外层 wrapper 若没同步状态,:focus-within就查不到活跃后代 - 别在深层嵌套容器(如
<div><div><div><input></div></div></div>)上盲目加,容易多层同时高亮,视觉混乱
tabindex="-1" 或漏掉 box-sizing: border-box,再简洁的 CSS 也白搭。


















