Explaining CSS Specificity Rules
Explaining CSS Specificity Rules
CSS 特异性(Specificity)规则详解
While working on my recent article, I had a dropdown set to display: block; only when the user hovers over it. The problem is that the dropdown still shows regardless. It was not removed from the viewport despite having display: none; applied. I inspected the dropdown in the browser’s dev tools and found display: none; — it was struck through.
在撰写最近的一篇文章时,我设置了一个下拉菜单,仅在用户悬停时显示为 display: block;。但问题是,无论如何下拉菜单始终显示。尽管应用了 display: none;,它并没有从视口中消失。我在浏览器的开发者工具中检查了该下拉菜单,发现 display: none; 被划掉了。
Upon further inspection, I discovered the parent <nav> had a display: flex; declaration using the .navbar ul selector. The dropdown was a <ul> with a class of .dropdown-menu set to display: none, but the style had no effect. Alas! .navbar ul used both a class and an element selector, giving it a specificity of 0, 0, 1, 1, compared to .dropdown-menu with a specificity of 0, 0, 1, 0.
经过进一步检查,我发现父级 <nav> 使用 .navbar ul 选择器声明了 display: flex;。该下拉菜单是一个带有 .dropdown-menu 类的 <ul>,虽然设置了 display: none,但该样式无效。唉!.navbar ul 同时使用了类选择器和元素选择器,其特异性为 0, 0, 1, 1,而 .dropdown-menu 的特异性仅为 0, 0, 1, 0。
CSS, which stands for Cascading Style Sheets, is a rulebook that defines the styles of a markup language, such as HTML and XML. The “cascading” in the name refers to the set of rules the browser follows to resolve naming conflicts. It works by considering three factors, one of which is selector specificity, the focus of this article. CSS(层叠样式表)是一本定义标记语言(如 HTML 和 XML)样式的规则手册。名称中的“层叠”指的是浏览器用于解决命名冲突的一套规则。它通过考虑三个因素来运作,其中之一就是选择器特异性,这也是本文的重点。
What is CSS Specificity?
什么是 CSS 特异性?
CSS specificity is a scoring system or a tie-breaking algorithm that helps the browser determine which selector takes precedence over others. It is the most common reason for style conflicts in development. You see, when writing CSS, you can mistakenly apply more than one style declaration to a single element. It might be with a class selector, an ID selector, an element selector, or even an inline style. CSS 特异性是一种评分系统或决胜算法,帮助浏览器确定哪个选择器具有优先权。它是开发中最常见的样式冲突原因。在编写 CSS 时,你可能会错误地对同一个元素应用多个样式声明,这可能是通过类选择器、ID 选择器、元素选择器,甚至是内联样式实现的。
<style>
p { color: blue; }
.greeting { color: green; }
#greeting-text { color: rebeccapurple; }
</style>
<p class="greeting" id="greeting-text" style="color: red">Hello</p>
When the browser encounters all these declarations, each targeting a single element, it uses selector specificity to determine which declaration wins and which are ignored. As you’ll see in the next section, each selector has a rank, which the browser calculates, and the selector with a higher score wins. 当浏览器遇到所有这些针对同一个元素的声明时,它会使用选择器特异性来决定哪个声明胜出,哪些被忽略。正如你将在下一节中看到的,每个选择器都有一个浏览器计算出的等级,得分更高的选择器获胜。
Specificity is just one of the cascade rules. When declarations collide, as in the scenario above, the cascade considers three factors to determine the winner: stylesheet origin, selector specificity, and source order. 特异性只是层叠规则之一。当声明发生冲突时(如上述场景),层叠机制会考虑三个因素来确定胜者:样式表来源、选择器特异性和源码顺序。
Stylesheet Origin
样式表来源
This is the first checkpoint. You see, styles are sorted into layers of origin, which are grouped into: user-agent styles (browser’s default styles), user styles (the custom external styles you load in your app), and author styles (the ones you wrote), in order of increasing precedence. A declaration in any origin will be preferred over those lower than it, but there’s an exception — !important. When you add !important to your declaration, it attains the highest priority, so the browser ignores every other declaration and uses that declaration.
这是第一个检查点。样式按来源层级进行排序,分为:用户代理样式(浏览器默认样式)、用户样式(你在应用中加载的自定义外部样式)和作者样式(你自己编写的样式),优先级依次递增。任何来源的声明都会优先于比它级别低的声明,但有一个例外——!important。当你为声明添加 !important 时,它将获得最高优先级,浏览器会忽略所有其他声明而使用该声明。
Source Order
源码顺序
This is reserved for the last. If two rules have the same origin and specificity, the source order is used as the final tie-breaker. It implements a simple rule: the last declaration wins. Whichever rule is defined last in the stylesheet (or loaded last if using multiple files) will be applied. 这是最后的手段。如果两条规则具有相同的来源和特异性,源码顺序将作为最终的决胜因素。它遵循一个简单的规则:最后定义的声明胜出。无论哪条规则在样式表中最后定义(或在使用多个文件时最后加载),它都将被应用。
/* Specificity: 0,1,0 (one class) */
.my-box { color: blue; }
/* Specificity: 0,1,0 (one class) */
.my-box { color: red; }
/* The box will be red, because this rule was defined last. */
/* 盒子将显示为红色,因为这条规则是最后定义的。 */
The Four Categories Of Specificity And Their Scores
特异性的四个类别及其分数
CSS has four categories of specificity, denoted by the common 0, 0, 0, 0 for scoring. Sometimes, the (A, B, C, D) notation is used, but it’s less popular than the former. In this notation, the first 0 represents inline styles. CSS 有四个特异性类别,通常用 0, 0, 0, 0 来表示评分。有时也会使用 (A, B, C, D) 记法,但不如前者流行。在这种记法中,第一个 0 代表内联样式。
<p style="color: red">Hello</p>
Since they are applied directly to the element, they form a scoped declaration that overrides any declaration to the same element within the CSS file or the <style> tag. The only exception, of course, is if a declaration targeting the same element in the stylesheet has !important — in this case, it overwrites the inline style. They have a score of 1, 0, 0, 0.
由于它们直接应用于元素,它们形成了一个作用域声明,会覆盖 CSS 文件或 <style> 标签内针对同一元素的任何声明。当然,唯一的例外是如果样式表中针对同一元素的声明带有 !important,在这种情况下,它会覆盖内联样式。它们的得分为 1, 0, 0, 0。
Inline styles are not categorised as selector specificity. The styles are applied directly to the element via the style attribute, not within the <style> tag or a CSS file. For simplicity, some authors restrict the score notation to the stylesheet or within the <style> tag, making it 0, 0, 0, instead of 0, 0, 0, 0. So, wherever you see the triple-digit notation, always know that the first 0 is for the ID selector.
内联样式不属于选择器特异性。这些样式是通过 style 属性直接应用于元素的,而不是在 <style> 标签或 CSS 文件中。为了简化,一些作者将评分记法限制为样式表或 <style> 标签内,使其变为 0, 0, 0 而不是 0, 0, 0, 0。因此,无论你在哪里看到三位数的记法,都要知道第一个 0 代表 ID 选择器。
The second 0 represents the ID selector. Within a stylesheet or the <style> tag, it has the highest specificity. It has a score of 0, 1, 0, 0, and will override every other selector, except a selector that has a declaration with !important.
第二个 0 代表 ID 选择器。在样式表或 <style> 标签内,它具有最高的特异性。它的得分为 0, 1, 0, 0,并将覆盖除带有 !important 声明的选择器之外的所有其他选择器。
#nav-menu { display: flex; }
The third 0 is for classes (.dropdown), pseudo-classes (:hover), and attribute selectors ([type="text"]). These three selectors fall into the same category and have the same score: 0, 0, 1, 0.
第三个 0 代表类(.dropdown)、伪类(:hover)和属性选择器([type="text"])。这三个选择器属于同一类别,得分相同:0, 0, 1, 0。
So, what happens when you run into this issue below: 那么,当你遇到下面这个问题时会发生什么:
<style>
.dropdown-link { color: green; }
[href="/products"] { color: blue; }
</style>
<a class="dropdown-link" href="/products">Products</a>
Since they have the same specificity score of 0, 0, 1, 0, which declaration will be applied? What will be the color of the link tag’s text? If you choose blue, you’re absolutely right! If you remember correctly, I mentioned source order in the previous section; It comes into play in this scenario. The last declaration overwrites the previous one if both selectors have the same specificity and origin. You can see it’s a bit tricky. Once you’re familiar with the rules, there’s nothing to worry about. 由于它们的特异性得分都是 0, 0, 1, 0,哪个声明会被应用?链接标签的文字颜色会是什么?如果你选择了蓝色,那完全正确!如果你还记得的话,我在上一节提到了源码顺序;在这种情况下它就起作用了。如果两个选择器具有相同的特异性和来源,最后的声明会覆盖之前的声明。你可以看出这有点棘手。一旦你熟悉了这些规则,就没什么可担心的了。
The last and final 0 goes to elements and pseudo-elements, like ul, p, ::before, ::after. This least-specificity ranking is reserved for selectors that target element types. They have a score of 0, 0, 0, 1, and any other selector’s declaration will override theirs, except, of course, if they have !important.
最后一个 0 代表元素和伪元素,如 ul、p、::before、::after。这个最低特异性等级是为针对元素类型的选择器保留的。它们的得分为 0, 0, 0, 1,任何其他选择器的声明都会覆盖它们,当然,除非它们带有 !important。