:has()用于根据子元素存在控制父/同级样式,:is()用于简化多个等价选择器的书写;前者解决反向选择,后者解决重复声明,二者不可互换。

:has() 和 :is() 解决的是完全不同的问题——选错一个,样式就不起作用,而且很难 debug。
什么时候必须用 :has()?
只有当你需要「根据子元素是否存在来控制父/同级元素样式」时,:has() 是唯一选择。CSS 原生不支持反向选择,过去只能靠 JS 或冗余 class 实现,现在靠它一步到位。
-
nav:has(.dropdown):给有下拉菜单的导航项加下划线 -
form:has(input:invalid):整个表单变红边框,提示用户存在错误输入 -
article:has(figure img):仅当文章含图时,调整段落首行缩进
注意::has() 不支持伪元素(如 ::before)、不支持 :focus-within 这类状态伪类嵌套,也不能写成 div:has(:has(span)) —— 只允许一层 :has()。
什么时候该用 :is()?
当你想「简化多个相似选择器的重复书写」,且这些选择器语义等价、优先级需保留时,:is() 是最稳妥的压缩方案。
-
h1:is(.title, .hero-title, [data-role="headline"])等价于分别写三个规则,但只算一次优先级 -
button:is(:hover, :focus-visible, .active)统一 hover/focus/active 样式,避免重复声明
关键点::is() 会取括号内所有选择器中**最高优先级**作为整条规则的优先级。比如 div:is(.foo, #bar) 的优先级等于 div#bar(id 级别),不是 class 级别。
别把 :is() 当 :has() 用
这是新手最容易踩的坑:以为 ul:is(li.active) 能选中包含 .active 的 ul —— 实际上它匹配的是「本身是 ul 且同时是 li.active 的元素」,这根本不可能成立。
-
:is()匹配的是「当前元素自己是否满足任一条件」 -
:has()匹配的是「当前元素内部是否存在满足条件的后代/兄弟」 - 浏览器报错不会直接说“你混用了”,而是静默失效 —— 控制台没警告,DevTools 里也找不到匹配项
如果发现样式没生效,先检查 DevTools 的 Elements 面板里目标元素是否被高亮;没高亮,大概率是逻辑误用,而不是兼容性问题。
兼容性不是主要障碍,但得知道边界
截至 2026 年 8 月,:has() 在 Chrome 105+、Firefox 120+、Safari 15.4+ 已稳定支持;:is() 更早(Chrome 88+ 就已可用)。真正要小心的是 Safari 对复杂 :has() 的限制:
- Safari 不支持
:has(~ .sibling)(通用兄弟)这种写法,但:has(+ .next)(相邻兄弟)可以 -
:has()里不能用:nth-child()等结构性伪类(部分 Safari 版本会忽略整条规则) - 服务端渲染(SSR)或静态站点生成器(如 Astro、Next.js)若未启用客户端 hydration,
:has()样式可能初始不触发 —— 因为 DOM 尚未完全挂载
最常被忽略的一点::has() 的相对选择器路径里,>(子选择器)和空格(后代)行为一致;但如果你依赖精确层级,最好用空格,因为 > 在某些旧版 Safari 中解析不稳定。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











