组合符和 :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 nothingp— 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:
| Combinator | Meaning | Counts |
|---|---|---|
A B (space) | descendant | No |
A > B | direct child | No |
A + B | next sibling | No |
A ~ B | subsequent siblings | No |
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
| Syntax | Counts toward specificity |
|---|---|
>, +, ~, space | No, relationship only |
The :not() wrapper | No |
Selectors inside :not() | Yes, scored normally |
One line: what can match an element scores; what only connects or wraps does not.