先记住这个答案
:not() 本身不贡献任何优先级。它的权重完全由括号里的参数决定:如果参数是类选择器 .disabled,优先级就是 (0, 1, 0);如果参数是 ID 选择器 #main,优先级就是 (1, 0, 0)。多个参数时取最高的那个,比如 :not(.a, #b) 的优先级是 (1, 0, 0),因为 #b 比 .a 高。:is() 和 :has() 的计算规则一样,唯独 :where() 是例外——不管里面写什么,整体优先级永远是零。
:not()自身不贡献优先级- 权重等于参数中最高的那个选择器
:is()和:has()规则相同:where()是例外,整体权重永远为零
参数决定权重
:not() 常用来排除某些元素,比如给所有不带 .disabled 的按钮加样式。这时候 :not(.disabled) 的优先级就等于 .disabled 的优先级。
/* :not(.disabled) → 优先级 (0, 1, 0)
* 和直接写 .disabled 一样 */
button:not(.disabled) {
cursor: pointer;
}
/* :not(#sidebar) → 优先级 (1, 0, 0)
* 参数是 ID 选择器,权重直接升到 ID 列 */
div:not(#sidebar) {
padding: 16px;
}
/* :not(.a, #b) → 优先级 (1, 0, 0)
* 多个参数取最高的 #b */
p:not(.a, #b) {
margin: 0;
}注意第三个例子:多参数时不是把 .a 和 #b 的权重加起来,而是只取最高的。这个规则和 :is() 完全一致。日常开发中要小心,如果 :not() 的参数里不经意写了一个 ID 选择器,整个选择器的优先级会被拉到 ID 级别,后续覆盖起来很麻烦。
实际项目中,:not() 最常见的参数是类选择器,比如 :not(.hidden) 或 :not(.disabled),这样优先级只在类列加 1,和直接写一个类一样,覆盖起来没什么负担。
上面例子的完整优先级还要算上 :not() 外面的部分。button:not(.disabled) 是 (0, 1, 1)——button 在元素列加 1,:not(.disabled) 在类列加 1。div:not(#sidebar) 是 (1, 0, 1)——:not(#sidebar) 在 ID 列加 1,div 在元素列加 1。
四个函数伪类的对比
CSS 有四个函数伪类和优先级相关,规则各不相同。
:not() 取参数中最高优先级的选择器,自身不计入。:is() 规则和 :not() 完全一样,区别只在语义——:is() 是匹配列表中的任意一个,:not() 是排除列表中的所有。
:has() 是近几年新加的关系伪类,根据后代元素来选祖先。优先级计算和 :not() 一样,取参数中最高的。比如 div:has(> .active) 的优先级包含 .active 的 (0, 1, 0) 加上 div 的 (0, 0, 1),结果是 (0, 1, 1)。
:where() 是专门为降低优先级设计的。不管参数里写了什么,:where() 整体优先级是零。:where(#id, .class, div) 的优先级是 (0, 0, 0)。Tailwind CSS 和一些组件库在预设样式里大量使用 :where(),就是为了让业务代码用普通类选择器就能轻松覆盖。
这四个函数伪类的优先级规则很容易混,面试时直接说「:not() 和 :is() 以及 :has() 取参数最高值,:where() 永远为零」就够了。记住 :where() 是唯一的零优先级伪类即可,其余三个规则相同。
容易答错的地方
- 以为
:not()自身也算一个伪类的优先级 - 普通伪类比如
:hover或:focus在类列加 1。但:not()不是普通伪类,它本身不贡献优先级。a:not(.disabled)的优先级是(0, 1, 1)而不是(0, 2, 1)。只有括号里的参数算。 - 多参数时把权重加在一起
:not(.a, .b)的优先级不是(0, 2, 0),而是(0, 1, 0)。多参数取的是最高值,不是累加。这个规则对:is()和:has()也一样。
面试官还会怎么问?
:not() 可以嵌套吗?
可以。:not(:not(.active)) 在语法上合法,等价于 .active,优先级也是 (0, 1, 0)。但写法绕,实际项目中没人这么用。
为什么 :where() 的优先级是零?
规范设计它就是为了「只做匹配,不抬权重」。组件库的预设样式用 :where() 包裹,业务代码随便写一个 .class 就能覆盖,不用打 !important。
:not() 和 :is() 在优先级上有什么区别?
没有区别。两者的优先级计算规则完全一样,都是取参数中最高优先级的选择器。区别只在匹配逻辑::is(.a, .b) 匹配 .a 或 .b 中的任意一个,:not(.a, .b) 排除 .a 和 .b 中的所有。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。