CSS 伪类函数 :where 简介

:where() 是一个非常有用的 CSS 伪类函数 (CSS functional pseudo-class),它允许你将一组选择器打包在一起,并具有一个关键特性:它的特异性(Specificity)总是为零(0)

打开 antd 的开发者模式,发现 antd 就用了不少这样的样式:

看一下他的兼容性,大部分主流浏览器都可以了:

一、:where() 的基本概念

:where() 是一个函数式伪类,它接受一个逗号分隔的选择器列表 作为参数。它会选中任何能被该列表中的任一选择器所匹配的元素。

语法:

css 复制代码
:where(<选择器列表>) {
  /* CSS 样式 */
}

示例:简化选择器 假设你希望同时给 h1h2h3 标签设置一个相同的 margin-top 样式,传统写法是:

css 复制代码
/* 传统写法 */
h1, h2, h3 {
  margin-top: 1.5em;
}

使用 :where() 后,可以写成如下的形式:

css 复制代码
/* 使用 :where() */
:where(h1, h2, h3) {
  margin-top: 1.5em;
}

二、:where() 的核心特性:特异性为零(Specificity: 0)

这是 :where() 与其孪生兄弟 :is() 最大的区别和价值所在。

  • :where() 的特异性: 始终是 0, 0, 0 。无论你传入的内部选择器有多复杂(例如,包含 ID 或类选择器),:where() 本身都不会贡献任何特异性值。
  • :is() 的特异性: 取决于其参数列表中特异性最高的那个选择器

特异性是 CSS 的一个核心概念,它是一个算法,用于确定当多个 CSS 规则试图为同一个元素设置相同的样式属性时,哪个规则应该"获胜"并应用其样式。

简单来说,特异性就是衡量一个 CSS 选择器有多精确的权重值。特异性高的选择器会覆盖特异性低的选择器,即使特异性低的规则在样式表的后面。

特异性(Specificity)决定了在同一个优先级层级(Cascade Layer 和 Origin)

特异性通常以三位数字(或四位,但通常是三位)来计算,例如 (A, B, C)。比较规则是从左到右:一个 ID ((0, 1, 0)) 永远比 100 个类 ((0, 0, 100)) 更具特异性。

特异性对比示例:

选择器 特异性分数 (ID, Class/Pseudo, Element)
h1 0, 0, 1
.main 0, 1, 0
:where(h1, .main) 0, 0, 0
:is(h1, .main) 0, 1, 0 (取 .main 的最高特异性)

!important 的特异性是0,但是!important 优先级位于特异性之上。

但是要避免滥用,因为他会破坏 CSS 级联(Cascade)和特异性(Specificity)的自然流,并且会使得样式难以追溯来源,从而增大调试难度。此外还会限制代码的灵活性和可重用性,别人想要用你的组件,就被迫使用优先级更高的!important来覆盖你的!important。

零特异性的应用场景

零特异性使 :where() 成为定义基础样式重置样式的完美工具。

  1. 基础样式/重置样式 (Baseline Styling): 当你定义一个全局或组件级的默认样式时,你希望它能够被任何其他普通的类选择器轻易覆盖,而不需要引入 !important 或增加额外的特异性。

    css 复制代码
    /* 所有的链接和按钮,默认颜色为蓝色 */
    :where(a, button) {
      color: blue; /* 特异性为 0,0,0 */
    }
    
    /* 此时,一个简单的类就可以轻松覆盖上面的基础样式 */
    .cta-button {
      color: red; /* 特异性为 0,1,0,轻松获胜 */
    }
  2. 避免特异性膨胀: 如果使用 :is(a, button),它的特异性是 0,0,1(取 abutton 的特异性),会增加后续覆盖样式的难度。而 :where() 则完全消除了这种顾虑。

这就是为什么很多组件库,例如 antd 会这样用

三、:where() 的其他优势

1. 简化复合选择器

:where() 可以用在选择器链中的任何位置,极大地简化了复杂的嵌套选择器。

简化前:

css 复制代码
/* 给侧边栏和页脚内的所有链接设置样式 */
.sidebar a,
.footer a {
  text-decoration: none;
}

简化后:

css 复制代码
:where(.sidebar, .footer) a {
  text-decoration: none;
}

2. 容错性(Forgiving Parsing)

与传统的逗号分隔选择器列表不同,:where()(以及 :is())接受一个可容错的选择器列表。

  • 传统列表: 如果列表中的任何一个选择器是无效的(例如,使用了旧浏览器不支持的最新特性),整个规则块都会被忽略

    css 复制代码
    h1, :unsupported-selector { /* 如果 :unsupported-selector 无效,h1 的样式也会失效 */
      color: red;
    }
  • :where()/:is() 列表: 如果列表中的一个选择器是无效的,浏览器只会忽略该无效的选择器 ,而继续使用列表中的其他有效选择器。

    css 复制代码
    :where(h1, :unsupported-selector) { /* 浏览器会忽略 :unsupported-selector,但 h1 的样式仍然有效 */
      color: red;
    }

四、:where():is() 的选择

特性 :where(<选择器列表>) :is(<选择器列表>)
功能 选中列表中任一匹配的元素 选中列表中任一匹配的元素
特异性 始终为零 (0,0,0) 取参数列表中最高特异性
主要用途 定义基础样式重置样式,确保样式易于覆盖。 简化选择器语法,同时保留/提升特异性。

使用场景:

  • 如果你想定义一个容易被覆盖 的默认样式,请使用 :where()
  • 如果你只是想简化 冗长的选择器,并且希望保持提升 特异性(以覆盖其他样式),请使用 :is()

五、在 css module 中使用

在 CSS Modules 中使用 :where() 伪类的方式与普通 CSS 基本一致,但需要注意 CSS Modules 的类名哈希化特性对选择器的影响。以下是具体用法和注意事项:

1. 作用于全局选择器(不被哈希化的类名)

如果需要用 :where() 匹配全局样式类 (未被 CSS Modules 哈希化的类名,通常通过 :global() 声明),直接使用即可:

css 复制代码
/* 组件的 CSS Modules 文件(如 Button.module.css) */
/* 全局类名 .btn-global 不被哈希化 */
:global(.btn-global) {
  padding: 8px;
}

/* 用 :where() 聚合全局选择器 */
:where(:global(.btn-global), :global(.link-global)) {
  color: blue;
}

编译后,:where() 会保留对全局类名的匹配,不受哈希影响。

2. 作用于本地选择器(被哈希化的类名)

如果 :where() 内部包含本地类名 (会被 CSS Modules 哈希化,如 .btn 编译为 btn__123),需要注意:

CSS Modules 会对 :where() 内部的本地类名进行哈希化,但 :where() 的特异性依然生效。

示例:

css 复制代码
/* Button.module.css */
/* 本地类名 .btn 和 .link 会被哈希化 */
.btn {
  border: 1px solid #ccc;
}

.link {
  text-decoration: none;
}

/* 用 :where() 聚合本地类名 */
:where(.btn, .link) {
  font-size: 14px;
}

编译后(假设哈希后缀为 __abc),实际生成的 CSS 为:

css 复制代码
.btn__abc {
  border: 1px solid #ccc;
}

.link__abc {
  text-decoration: none;
}

:where(.btn__abc, .link__abc) {
  font-size: 14px;
}

此时,:where(.btn__abc, .link__abc) 会匹配哈希化后的类名,且特异性为 0。

3. 混合全局和本地选择器

:where() 中可以同时包含全局和本地选择器,CSS Modules 只会哈希化本地类名:

css 复制代码
/* Button.module.css */
.localClass {
  margin: 0;
}

/* 混合全局和本地选择器 */
:where(.localClass, :global(.globalClass)) {
  padding: 0;
}

编译后:

css 复制代码
.localClass__def {
  margin: 0;
}

:where(.localClass__def, .globalClass) {
  padding: 0;
}

完~~

相关推荐
醉城夜风~1 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子4 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅4 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni4 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
开普勒chr6 小时前
css样式diaplay:"contents"的妙用
css
赵庆明老师6 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc886 小时前
微软EDGE浏览器功能学习
前端·学习·edge