2025年,每个前端都应该了解的CSS选择器:`:has()`, `:is()`, `:where()`

干前端的,天天都在跟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 正在变得越来越强大,也越来越有逻辑性。拥抱这些新特性,不仅能让我们的代码更优雅,也能让我们换个角度去思考布局和交互的实现方式。谢谢大家🙂

相关推荐
菌菇汤8 分钟前
uni-app实现单选,多选也能搜索,勾选,选择,回显
前端·javascript·vue.js·微信小程序·uni-app·app
Ramos丶16 分钟前
【ABAP】 从无到有 新建一个Webdynpro程序
java·前端·javascript
qq_4116719835 分钟前
vue3 的模板引用ref和$parent
前端·javascript·vue.js
清幽竹客2 小时前
vue-37(模拟依赖项进行隔离测试)
前端·vue.js
vvilkim2 小时前
Nuxt.js 页面与布局系统深度解析:构建高效 Vue 应用的关键
前端·javascript·vue.js
滿2 小时前
Vue3 父子组件表单滚动到校验错误的位置实现方法
前端·javascript·vue.js
夏梦春蝉3 小时前
ES6从入门到精通:模块化
前端·ecmascript·es6
拓端研究室4 小时前
视频讲解:门槛效应模型Threshold Effect分析数字金融指数与消费结构数据
前端·算法
工一木子5 小时前
URL时间戳参数深度解析:缓存破坏与前端优化的前世今生
前端·缓存
半点寒12W6 小时前
微信小程序实现路由拦截的方法
前端