:has(:focus)不生效主因是浏览器兼容性不足或父选择器写法错误;需确保父元素使用:has(input:focus),且input可聚焦、未被shadow DOM隔离,Chrome105+/Safari15.4+/Firefox121+才支持。

为什么直接用 :has(:focus) 有时不生效
因为 :focus 只匹配当前获得焦点的元素,而如果子元素是 position: absolute 或 fixed,它可能脱离文档流,但焦点行为本身不受影响——问题通常出在选择器范围或 DOM 结构上。更常见的是:父容器没设 display: block 或被 overflow: hidden 截断,导致视觉上“没变”,其实样式已应用。
关键前提::has() 必须写在父选择器上,且浏览器需支持(Chrome 105+、Safari 15.4+、Firefox 121+)。不支持时整个规则会被忽略,不会报错。
-
:has()内部不能包含伪元素(如::before)或可动画属性(如transition),但可以嵌套简单伪类(:focus、:hover、:checked) - 若子元素是
input或textarea,确保它们没有tabindex="-1",否则无法触发:focus - 绝对定位元素不影响
:has()匹配逻辑,只要它仍是父元素的后代节点(不是被teleport或createPortal移走)
怎样让父容器在子输入框聚焦时高亮边框
这是最典型场景:一个带 label 和 input 的表单组,希望 input 获焦时整个 wrapper 变色。注意不要用 label:focus-within —— 它对点击 label 触发 focus 无效,而 :has(input:focus) 更可靠。
form.control-group:has(input:focus) {
border-color: #007aff;
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.2);
}
这里 form.control-group 是父容器,input 是其直系或深层后代。如果 input 在 shadow DOM 内,:has() 无法穿透,此时需改用 JavaScript 监听或把逻辑提到 light DOM。
- 若 input 是
position: absolute,只要仍在父元素内部(哪怕视觉上溢出),:has(input:focus)依然匹配 - 避免写成
:has(> input:focus),除非你确定 input 总是直系子元素;多数情况用空格更稳妥 - 不要在
:has()里加太多层级(如:has(div > span > input:focus)),性能会下降,且可读性差
如何配合 position: sticky 实现滚动时父级样式切换
:has() 本身不响应滚动,但它能响应滚动中因 position: sticky 触发的“粘住”状态变化——前提是该 sticky 元素有明确的聚焦能力(比如是 button 或加了 tabindex="0" 的 div)。
例如:导航栏内有个 sticky 按钮,滚动到顶部时它“吸顶”,你想让整个 header 变透明:
header:has(button.sticky-btn:focus) {
background: transparent;
backdrop-filter: blur(8px);
}
但更实际的做法是监听 button.sticky-btn:has(:focus-visible),避免鼠标点击时不触发。不过要注意:sticky 元素默认不可聚焦,必须手动加 tabindex="0"。
- 滚动本身不会改变 focus 状态,所以这个技巧本质是“用户主动聚焦 sticky 元素后,再滚动”,不是纯滚动响应
- 若想真正响应滚动位置,仍需
IntersectionObserver或 JS,:has()不替代 scroll event -
:focus-visible比:focus更安全,避免键盘用户和鼠标用户行为不一致
容易被忽略的兼容性与调试陷阱
开发时打开 DevTools 很难直观看到 :has() 是否匹配成功——它不像 :hover 那样能用“强制状态”模拟。最有效的方式是临时加一条 outline: 2px solid red 看是否触发。
- Firefox 旧版本(layout.css.has-selector.enabled 标志,生产环境不建议依赖
- Next.js / Remix 等 SSR 框架中,
:has()在服务端渲染时会被静默丢弃,导致首屏无样式,必须搭配useEffect或媒体查询降级 - PostCSS 插件(如
postcss-preset-env)目前**无法编译**:has(),别指望它能转成兼容写法——它就是个现代 CSS 原生特性,不降级
真正麻烦的不是语法,而是当你要同时支持 Safari 15.3 和 Chrome 104 时,:has() 就完全不可用,这时只能回退到 class 切换或 JS 监听。别试图用 feature query 包一层就完事,@supports selector(:has(*)) 的支持度比 :has() 本身还低。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











