CSS 播客 - 003:特异性
假设您正在使用以下 HTML 和 CSS:
<button class="branding">Hello, Specificity!</button>
.branding {
color: blue;
}
button {
color: red;
}
这里有两条规则针对的是同一元素。 每条规则都包含一个想要设置按钮颜色的声明:一条规则尝试将按钮着色为红色,另一条规则尝试将按钮着色为蓝色。哪个声明会应用于相应元素?
了解 CSS 特异性算法是了解 CSS 如何在相互冲突的声明之间做出选择的关键。
特异性是级联的独特阶段之一,我们在上一个模块中介绍了级联。
特异性得分
来源中的每个选择器规则都会获得一个得分。您可以将特异性视为总分,每种选择器类型都会为该总分贡献分数。 具有最高特指性的规则中的声明胜出。
在实际项目中,特异性方面的平衡在于确保您希望应用的 CSS 规则确实能够应用,同时通常保持较低的分数以防止复杂性。特异性应仅达到我们需要的程度,而不是尽可能地提高特异性。 未来,可能需要应用一些真正更重要的 CSS。 如果您追求最高特异性,那么这项工作就会变得非常困难。
特异性不是十进制数,而是由三个分量(A、B 和 C)组成的三元组。
A:类似 ID 的特异性B:类级特异性C:类似元素的特异性
它通常使用 (A,B,C) 表示法表示。例如:(1,0,2)。替代表示法 A-B-C 也很常用。
如何比较特指度
通过按顺序比较这三个组成部分来比较特异性:A 值较大的特异性更具体;如果两个 A 值相同,则 B 值较大的特异性更具体;如果两个 B 值也相同,则 C 值较大的特异性更具体;如果所有值都相同,则这两个特异性相同。
例如,(1,0,0) 的特异性高于 (0,4,3),因为 (1,0,0) 中的 A 值(即 1)大于 (0,4,3) 中的 A 值(即 0)。
选择器会影响特异性
特异性三元组中的每个部分都以 0 值开头,因此默认特异性为 (0,0,0)。选择器的每个部分都会提高特异性,具体取决于选择器的类型,从而增加 A、B 或 C 的值。
通用选择器
通用选择器 (*) 不会增加特异性,因此其值仍为初始特异性 (0,0,0)。
* {
color: red;
}
元素或伪元素选择器
元素(类型)或伪元素选择器会添加类似元素的特指度,从而使 C 组件递增 1。
以下示例的总体特异性为 (0,0,1)。
类型选择器
div {
color: red;
}
伪元素选择器
::selection {
color: red;
}
类选择器、伪类选择器或属性选择器
类、伪类或属性选择器会添加类似类的特异性,从而使 B 组件递增 1。
以下示例的特异性为 (0,1,0)。
类选择器
.my-class {
color: red;
}
伪类选择器
:hover {
color: red;
}
属性选择器
[href='#'] {
color: red;
}
ID 选择器
ID 选择器会添加类似 ID 的特异性,从而使 A 组件递增 1,前提是您使用的是 ID 选择器 (#myID) 而不是属性选择器 ([id="myID"])。
在以下示例中,特异性为 (1,0,0)
#myID {
color: red;
}
其他选择器
CSS 有许多选择器。并非所有这些修饰符都能提高具体性。
例如,:not() 伪类本身不会为特指度计算增加任何值。
:not() 伪类的特指度会被其以逗号分隔的参数(选择器)中最具体选择器的特指度所取代;提供的特指度与编写 :not(:is(argument)) 时相同。
div:not(.my-class) {
color: red;
}
:is() 伪类本身也不会增加特指度计算结果。与 :not() 类似,它采用最具体实参的特异性。
:is(h1, h2, h3) {
color: blue;
}
此示例的特异性为 (0,0,1),因为它只有一种选择器 (type)。
添加 id 后,特异性会增加到 (1,0,0)。
:is(h1, h2, h3, #my-heading) {
color: blue;
}
:where() 伪类的不同之处。无论其任何实参的特异性如何,它始终具有 (0,0,0) 的特异性。
:where(h1, h2, h3, #my-heading) {
color: blue;
}
使用 :where() 应用的低特异性样式可让您稍后在样式表中通过基本选择器替换这些样式:
:where(#my-content) {
color: red;
}
p {
color: blue;
}
即使 :where() 的实参列表中包含 id,它的特指度仍然只有 (0, 0, 0),因此基本 p 选择器会覆盖它,文本将为 blue。
检查掌握情况
测试您对特异性得分的了解程度
a[href="#"] 的特异性是多少?
(0,0,1)a 的价值为 (0,0,1),但 [href="#"] 的价值为 (0,1,0)。(0,1,0)a 的价值为 (0,0,1),但 [href="#"] 的价值为 (0,1,0)。(0,1,1)a 的值为 (0,0,1),[href="#"] 的值为 (0,1,0),总特异性为 (0,1,1)。不影响特异性的因素
以下因素会影响特异性,但人们对这些因素存在一些常见误解。
内嵌样式属性
直接应用于元素 style 属性的 CSS 不会影响特异性,因为它是层叠样式表中的一个不同步骤,会在特异性之前进行评估。
<div style="color: red"></div>
为了从样式表中替换此声明,您必须在层叠样式表的早期步骤中获取声明胜出。
例如,您可以向其添加 !important,使其成为已添加 !important 的来源的一部分。
!important 项声明
CSS 声明末尾的 !important 不会影响特异性,但会将声明置于不同的来源(即作者 !important)。
在以下示例中,.my-class 的特殊性与 !important 声明的胜出无关。
.my-class {
color: red !important;
color: white;
}
如果两个声明均为 !important,则特异性会再次发挥作用,因为级联中的来源步骤尚未确定胜出者。
.branding {
color: blue !important;
}
button {
color: red !important;
}
上下文中的特异性
使用复杂或复合选择器时,该选择器的每个部分都会增加特异性。 请看以下 HTML 示例:
<a class="my-class another-class" href="#">A link</a>
此链接包含两个类。
以下 CSS 中的规则具有 (0,0,1) 的特异性:
a {
color: red;
}
如果您在选择器中引用了某个类,则该类现在具有 (0,1,1) 的特异性:
a.my-class {
color: green;
}
将另一个类添加到选择器,现在它的特异性为 (0,2,1):
a.my-class.another-class {
color: rebeccapurple;
}
将 href 属性添加到选择器,现在该选择器的特异性为 (0,3,1):
a.my-class.another-class[href] {
color: goldenrod;
}
最后,为所有这些内容添加 :hover 伪类,选择器的特异性最终为 (0,4,1):
a.my-class.another-class[href]:hover {
color: lightgrey;
}
检查掌握情况
测试您对特异性得分的了解程度
以下哪个选择器的特异性为 (0,2,1)?
article > section(0,0,2)。article.card.dark(0,2,1)。article:hover a[href](0,0,1))、1 个属性选择器(价值 (0,1,0))和 1 个类选择器(价值 (0,1,0))。因此,此选择器的总特异性为 (0,2,2)。以实用方式提高特指度
假设您有一些如下所示的 CSS:
.my-button {
background: blue;
}
button[onclick] {
background: grey;
}
HTML 代码如下所示:
<button class="my-button" onclick="alert('hello')">Click me</button>
该按钮具有灰色背景,因为第二个选择器的特异性为 (0,1,1)。这是因为该选择器包含一个类型选择器 (button),即 (0,0,1),以及一个属性选择器 ([onclick]),即 (0,1,0)。
之前的规则 .my-button 等于 (0,1,0),因为它有一个类选择器,其特指度低于 (0,1,1)。
如果您想提高此规则的优先级,可以重复使用类选择器,如下所示:
.my-button.my-button {
background: blue;
}
button[onclick] {
background: grey;
}
现在,该按钮将具有蓝色背景,因为新选择器的特异性为 (0,2,0)
如果特异性相同,则回退到级联中的下一步
现在继续使用按钮示例,将 CSS 切换为以下内容:
.my-button {
background: blue;
}
[onclick] {
background: grey;
}
由于两个选择器的特指度相同,均为 (0,1,0),因此按钮的背景为灰色。
如果您按来源顺序切换规则,该按钮随后会变为蓝色。
[onclick] {
background: grey;
}
.my-button {
background: blue;
}
这是因为这两个选择器的特异性相同。在这种情况下,级联会回退到显示顺序步骤。