干前端的,天天都在跟CSS打交道。以前我们写CSS,要是遇到复杂点的场景,比如"当一个卡片里有图片时,给卡片加个边框",或者要给一长串不同的标题应用同一个样式,那代码写起来,说实话,挺啰嗦的。
但时代变了。CSS自己也越来越"聪明"。
今天想聊的这三个选择器------:has()
, :is()
, :where()
,可能有些同学还不太熟,但我敢说,一旦你开始用它们,绝对会有一种"卧槽,原来CSS可以这么写?"的舒爽感。它们不是什么花里胡哨的技能,而是能实实在在提升我们日常编码效率和代码可读性的"神器"。
1. :has()
- 那个我们盼了很久的"父选择器"
:has()
绝对是这三个里面最重要的选择器。我们一直想要一个能根据子元素来选择父元素的方法,现在,它来了。
简单说,:has(selector)
的意思是:选择包含selector
所匹配元素的那个元素。
听起来有点绕,直接看例子。
场景:当卡片内有图片时,改变卡片样式
以前我们怎么写?
我们没法用CSS直接判断,通常得用JS去遍历DOM,给有图片的卡片加个class。
JavaScript
// JS to add a class
document.querySelectorAll('.card').forEach(card => {
if (card.querySelector('img')) {
card.classList.add('card-with-image');
}
});
CSS
/* Then style the class */
.card-with-image {
border: 2px solid #61dafb;
}
这太麻烦了,样式的问题还得JS来帮忙。
现在用 :has()
怎么写?(一行CSS搞定)
CSS
/* Select any .card that has an <img> inside it */
.card:has(img) {
border: 2px solid #61dafb;
padding: 10px;
}
看到了吗?就这么简单。 当.card
元素"拥有"一个img
子孙时,这个样式就生效了。
更多玩法:
-
表单校验高亮 :当输入框后面紧跟着一个
.error
提示时,让输入框变红。CSS/* Select an input that has an adjacent sibling .error */ input:has(+ .error-message) { border-color: red; }
-
布局调整:当一个容器里有超过5个子项时,改变布局。
CSS/* Select a .grid-container that has at least 6 children */ .grid-container:has(div:nth-child(6)) { grid-template-columns: repeat(3, 1fr); /* 切换到三列布局 */ }
:has()
的出现,让CSS的逻辑表达能力提升了一个维度,很多以前必须JS出马的场景,现在CSS自己就能搞定。
2. :is()
和 :where()
- 别再写一长串重复的选择器了
这两长得很像,功能也几乎一样,我们放一起说。它们的作用都是 "匹配选择器列表中的任何一个",可以极大地简化我们的代码。
场景:给文章里的所有标题(h1-h6)应用统一样式
以前我们怎么写?(又长又臭)
为了覆盖所有标题,我们得把它们一个个列出来。
CSS
article h1,
article h2,
article h3,
article h4,
article h5,
article h6 {
color: #333;
margin-bottom: 1em;
}
要是想给这些标题的hover
状态加个样式,还得再把上面这坨复制一遍......想想就头大。
现在用 :is()
怎么写?(清爽多了)
CSS
/* One selector to rule them all */
article :is(h1, h2, h3, h4, h5, h6) {
color: #333;
margin-bottom: 1em;
}
/* Hover state is also super clean */
article :is(h1, h2, h3, h4, h5, h6):hover {
color: #61dafb;
}
那 :where()
是干嘛的?它和 :is()
有啥区别?
功能上,where()
和 is()
一模一样。上面那个例子,你把is
换成where
,效果完全不变。
它们唯一的区别,也是最关键的区别,在于"权重" (Specificity)。
:is()
的权重,等于它括号里所有选择器中,权重最高的那个。:where()
的权重,永远是 0。
这是什么意思?看个例子。
CSS
:is(header, #main) p { color: blue; } /* #main的权重是100,所以这条规则权重是101 */
:where(header, #main) p { color: red; } /* :where权重是0,所以这条规则权重是1 */
p { color: green; } /* p的权重是1 */
- 最终,页面里的
<p>
会是blue
,因为101 > 1
。 - 如果把
:is
那行去掉,<p>
就会是green
,因为:where
那条规则的权重是1
,和p
一样,遵循"后来者居上"的原则。
什么时候用哪个?
- 当你只是想简化选择器 ,并且希望它能正常地覆盖掉默认样式时,用
:is()
。 - 当你希望提供一个 "可被轻松覆盖"的默认样式 时,用
:where()
。比如你在写一个基础样式库,希望用户可以非常容易地用他们自己的简单选择器(比如p { color: ... }
)来覆盖掉你库里的样式,那么用:where()
就非常合适。它就像在说:"我这个样式,谁都能欺负"。
3. 浏览器兼容性怎么样?
聊了这么多,现在(2025年)到底能不能在项目里用呢?
答案是:放心用!



截至目前,所有主流现代浏览器(Chrome, Firefox, Safari, Edge)的最新版本都已良好支持这三个选择器。
除非你还需要兼容非常非常老的浏览器(比如IE,那就算了......),否则在你的新项目中,大胆地用起来吧。
好了,今天就聊这么多。简单回顾一下:
:has()
:强大的"父选择器",让你能根据子元素状态来决定父元素样式,干掉了许多不必要的JS。:is()
:选择器"打包"工具,大幅简化重复的选择器列表,代码更干练。:where()
:和:is()
功能一样,但权重为0,特别适合用来写可被轻松覆盖的基础样式或默认样式。
CSS 正在变得越来越强大,也越来越有逻辑性。拥抱这些新特性,不仅能让我们的代码更优雅,也能让我们换个角度去思考布局和交互的实现方式。谢谢大家🙂