:has()是CSS Selectors Level 4规范的关系选择器,支持Chrome 105+、Edge 105+、Safari 15.4+、Firefox 121+,可基于子/兄弟元素存在性反向选中祖先或同级元素,但不支持伪元素、:not()内嵌套及旧版浏览器降级。

:has() 选择器目前(截至2024年中)**不是 CSS4 的特性,而是已正式纳入 CSS Selectors Level 4 规范,并已在主流浏览器中逐步落地的原生 CSS 功能**。它不依赖预处理器或 JS,但兼容性仍需谨慎对待——别一写就上线,先查支持情况。
哪些浏览器能用 :has()?
Chrome 105+、Edge 105+、Safari 15.4+(仅支持部分语法)、Firefox 121+(默认启用)。但注意:Safari 对嵌套 :has() 和某些伪类组合(如 :has(:focus-within))仍有 Bug;Firefox 在早期 121 版本中对 :has(+) 邻居组合符支持不完整。
验证方式:打开控制台执行 document.querySelector('div:has(p)'),不报错且返回预期元素,说明基础可用。
:has() 的基本写法和常见错误
语法是 父选择器:has(子选择器),只匹配「存在满足条件的后代/子元素」的父级。它不接受伪元素、:target 等非布局相关伪类,也不能出现在 :not() 内部(会直接失效)。
立即学习“前端免费学习笔记(深入)”;
-
正确:
article:has(> .highlight)(直接子元素) -
正确:
ul:has(li.active)(后代匹配) -
错误:
div:has(::before)(伪元素不被允许) -
错误:
:not(:has(p))(规范禁止在:not()中使用:has()) -
危险:
section:has(div + p)(Safari 15.4–16.3 中该组合符可能不触发重绘)
替代方案:当 :has() 不可用时怎么处理?
没有 JS 就无法可靠回退——这是它的硬限制。CSS 本身无法“向上选择”,所以必须换思路:
- 把状态类加到父级:比如 JS 监听
p是否存在,给article加has-paragraph类,再写article.has-paragraph { … } - 用属性选择器模拟:如果结构可控,可改用
[data-has-p]配合 JS 设置 - 避免过度依赖:例如「有按钮才显示边框」这类需求,优先考虑设计上让容器始终有语义化子元素,而非靠
:has()切换样式
顺带一提::has() 是**关系选择器**,浏览器需遍历 DOM 树做反向查找,大量使用(尤其在深层嵌套或高频重排场景)可能影响性能,别在 body:has(*) 这种宽泛选择上滥用。
真正麻烦的不是语法,而是它看起来像万能钥匙,结果在 Safari 里某个 :has(~) 组合突然不生效,又或者 Firefox 更新后旧写法被严格校验报错——上线前务必在目标浏览器中用真实 DOM 结构测试,而不是只看 Can I Use 的绿色勾。


















