← 返回博客

组合符和 :not() 一样,本身不计入优先级

看 CSS 优先级时有个规律:有些符号本身不计分,它们连接或包裹的选择器才计分。

>、+、~、空格,还有 :not(),都属于这一类。

组合符:只描述关系

div > p {
  color: red;
}

拆开看:

  • div —— 元素选择器,计分
  • > —— 只表示「直接子元素关系」,不计分
  • p —— 元素选择器,计分

所以 div > p 的优先级是 (0, 0, 2),和写成后代选择器完全一样:

div p {
  color: blue;
} /* 同样是 (0, 0, 2) */

区别只在匹配范围:> 只匹配直接子级,空格匹配所有后代层级。优先级上没有任何区别。

四种组合符都一样:

组合符含义计分
A B(空格)后代否
A > B直接子元素否
A + B紧邻的下一个兄弟否
A ~ B后面的所有兄弟否

因为组合符不是选择器 —— 它不匹配任何元素,只是在描述两边选择器的关系。没东西可匹配,自然没分可算。

:not():壳子不算,里面算

:not(#header) {
  color: red;
}
  • :not() 这个壳子 —— 不计分
  • #header(括号里的)—— ID 选择器,正常计分

所以这条是 (1, 0, 0),和直接写 #header 一模一样。

括号里放什么都行,都按自己的类型正常算:

p:not(.special)      /* (0, 1, 1) —— .special 算 b 位,p 算 c 位 */
div.card:not([hidden]) /* (0, 2, 1) —— .card 和 [hidden] 各算 b 位 */

其实是同一回事

div > .box

> 是关系符不计分,div 和 .box 才是真选择器,各自正常计分。

p:not(.special)

:not() 是排除符不计分,括号里的 .special 才是真选择器,正常计分。

组合符是站在两个选择器中间的符号,:not() 是把选择器包起来的符号。一个连接、一个包裹,作用不同,但都不计分。真正算分的,永远是那个能匹配到元素的选择器。

一个容易踩的坑

别以为写了 :not() 就能降低优先级 —— 它只是免费的外壳,里面的东西照样加分:

/* (0, 1, 1) —— 这条赢 */
p:not(.special) { color: red; }

/* (0, 1, 0) */
.special { color: blue; }

上面那条赢,因为 p 贡献了 c 位。加了 :not() 反而更「重」了,跟「排除」给人的直觉正好相反。

小结

语法计入优先级
>、+、~、空格否,只描述关系
:not() 的壳子否
:not() 括号里的选择器是,正常计分

一句话:能匹配到元素的才计分,只负责连接和包裹的不计分。

There is a pattern in specificity: some symbols are just "lead-ins" — they score nothing themselves, and the selector attached to them is what counts.

>, +, ~, space, and :not() all belong to this group.

Combinators only describe relationships

div > p {
  color: red;
}

Break it apart:

  • div — element selector, counts
  • > — means "direct child relationship", counts nothing
  • p — element selector, counts

So div > p scores (0, 0, 2) — identical to the descendant form:

div p {
  color: blue;
} /* also (0, 0, 2) */

The only difference is what matches: > matches direct children, a space matches every descendant level. Specificity is unaffected.

All four combinators behave the same:

CombinatorMeaningCounts
A B (space)descendantNo
A > Bdirect childNo
A + Bnext siblingNo
A ~ Bsubsequent siblingsNo

Because a combinator is not a selector — it matches no element, it only describes the relationship between the selectors on either side. Nothing to match, nothing to score.

:not() — wrapper free, contents scored

:not(#header) {
  color: red;
}
  • The :not() wrapper — counts nothing
  • #header (inside the parentheses) — an ID selector, scored normally

So this scores (1, 0, 0), exactly the same as writing #header alone.

Anything inside is scored by its own type:

p:not(.special)        /* (0, 1, 1) — .special is b, p is c */
div.card:not([hidden]) /* (0, 2, 1) — .card and [hidden] are both b */

It's the same story

div > .box

> is a relationship symbol and scores nothing; div and .box are the real selectors and each score normally.

p:not(.special)

:not() is an exclusion symbol and scores nothing; the .special inside is a real selector and scores normally.

A combinator sits between two selectors; :not() wraps one. One connects, one wraps — different jobs, both worth zero. What scores is always the selector that can match an element.

One easy trap

Don't assume :not() lowers specificity — it is a free wrapper, and its contents still add up:

/* (0, 1, 1) — this one wins */
p:not(.special) { color: red; }

/* (0, 1, 0) */
.special { color: blue; }

The first rule wins because p contributes to c. Adding :not() made it heavier, the opposite of what "exclusion" suggests.

Summary

SyntaxCounts toward specificity
>, +, ~, spaceNo, relationship only
The :not() wrapperNo
Selectors inside :not()Yes, scored normally

One line: what can match an element scores; what only connects or wraps does not.