在上一节中,我们学习了多种多样的CSS选择器。但在实际的工作中,我们可能还有些疑惑,当有多个选择器作用在一个元素上时,哪个规则最终会应用到元素上?其实这是通过层叠机制来控制的,这也和样式继承(元素从其父元素那里获得属性值)有关、在本节中中我们将定义什么是层叠,什么是特异,元素属性如何从不同的规则中继承以及其重要性。
In a previous article, we got into the various CSS selectors. At some point in your work, you'll find yourself in the situation where multiple CSS rules will have selectors matching the same element. In such cases, which CSS rule "wins", and ends up being the one that is finally applied to the element? This is controlled by a mechanism called the Cascade; this is also related to inheritance (elements will take some property values from their parents, but not others.) In this article we will define what the cascade is, what specificity is, what importance is, and how properties inherit from different rules.
前提条件 |
了解HTML基本知识,了解CSS 如何工作的。 Basic computer literacy, basic software installed, basic knowledge of working with files, HTML basics (study Introduction to HTML), and an idea of How CSS works (study the previous articles in this module.) |
---|---|
目标 |
了解层叠及特异,以及CSS中继承是如何实现的。 To learn about the cascade and specificity, and how inheritance works in CSS. |
元素的最终样式可以在多个地方定义,它们以复杂的形式相互影响。这些复杂的相互作用使CSS变得非常强大,但也使其非常难于调试和理解。这篇文章旨在明晰其中的部分复杂性;如果你不能立即理解,也没关系,这是CSS 理论中最复杂的地方。你可以现在就尝试学习,或在遇到关于层叠和继承问题的时候再来翻阅。
层叠
CSS 是 Cascading Style Sheets 的缩写,这暗示层叠(cascade)的概念是很重要的。在最基本的层面上,它表明CSS规则的顺序很重要,但它比那更复杂。什么选择器胜出的级联取决于三个因素(这些是为了上市体重早前将否决后的):
- 重要性(importance)
- 特殊性(specificity)
- 源代码次序
重要性
在CSS中,有一个特别的语法可以让一条规则 总是 优先于其他规则:!important
. 把它加在属性值的后面可以使这条规则有无比强大的力量。
让我们看一下这个例子:
<p class="better">This is a paragraph.</p> <p class="better" id="winning">One selector to rule them all!</p>
#winning { background-color: red; border: 1px solid black; } .better { background-color: gray; border: none !important; } p { background-color: blue; color: white; padding: 5px; }
这产生如下:
让我们一起通过这看到发生的事。
- 你可以看到第三条规则
color
和padding
被运用了, 但background-color
没有,为什么? 三条规则都的确被添加了, 但是在后面出现的规则覆盖了前面的。 - 然而, 在前面的规则被运用了,因为 IDs/class 选择器比element选择器有更高的优先级。 (对此,你将在下一章中学到更多)
- 每一个元素都有
class
并带有带有better
属性, 但是第二个元素有id
值为winning
。 因为 IDs 有更高 的优先级比起class(在一个页面上id是唯一的, 但很多元素可以拥有相同的class — ID 选择器对于它的所属元素来说最独特),红色背景色和1pixel的黑色边框都应应用于第二元素,第一个元素获得灰色背景色,没有边框,如类所指定。 - 第二个元素获得红色背景色,但没有边框。为什么?因为这个
!important
第二条规则中的声明——包括border: none
means 即使id具有更高的特异性,该声明也将赢得前面规则中的边界值。
注意: 超越这个的唯一方法 !
重要声明可以包含另一个!同一特殊性的重要声明,在源顺序之后。
知道这一点很有用!重要的是你在别人的代码中发现它时,你知道它是什么。但。我们建议你千万不要使用它,除非你绝对必须使用它。您可能需要使用的一种情况是,当您在CMS上工作时,您不能编辑核心CSS模块,而您真的要重写不能以任何其他方式重写的样式。但是,真的,不使用它,如果你能避免它。因为!重要的变化梯级正常工作的方式,它可以调试CSS问题很难解决,尤其是在一个大的样式表。
要注意一个CSS声明的重要性取决于它被指定在什么样式表为用户设置自定义样式表覆盖开发商的风格可能也是有用的,例如用户可能有视力障碍,想设置字体大小对所有网页的访问是双重的正常大小,以便更容易阅读。
相互矛盾的声明将按以下顺序适用,后一种则适用于先前的命令:
- 在用户代理样式表的声明 (e.g. the browser's default styles, used when no other styling is set).
- 用户样式表中的常规声明(由用户设置的自定义样式)。
- 作者样式表中的正常声明(这是我们设置的样式,Web开发人员)。
- 作者样式表中的重要声明
- 用户样式表中的重要声明
它为Web开发者的样式覆盖用户样式表是有意义的,这样的设计可以保持为目的,但有时用户有很好的理由去覆盖Web开发方式,如上所述,这可以通过使用!在他们的规则中很重要。
特殊性
特异性基本上是衡量选择器的具体程度——它能匹配多少元素。如上面所示的示例所示,元素选择器具有很低的特异性。类选择器具有更高的特殊性,因此将战胜元素选择器。id选择器具有更高的特殊性,因此会战胜类选择器。唯一战胜ID选择器的方法是使用!重要。
选择器的特殊性量是用四种不同的值(或组件)来衡量的,它们可以被认为是四个列中的四个数字:
- 如果匹配选择器位于一个内部,则在该列中取得一个
<style>
元素或声明内style
属性(这样的声明没有选择器,所以它们的特殊性通常是1000),否则为0。 - Hundreds: 在本专栏中为每个选择器中包含的每个ID选择器取得一个得分。
- Tens: 在这个列中为每一个类选择器、属性选择器或包含在整个选择器中的伪类取得一个分数。
- Ones: 在这个列中为每个元素选择器或包含在整个选择器中的伪元素得分。
注意: 通用选择器 (*
), 连接符 (+
, >
, ~
, ' ') 和否定伪类 (:not
) 有特异性无影响。
下表显示了几个孤立的示例,以使您的心情。试着通过这些,并确保你理解他们为什么具有我们给予他们的特殊性。
Selector | Thousands | Hundreds | Tens | Ones | Total specificity |
---|---|---|---|---|---|
h1 |
0 | 0 | 0 | 1 | 0001 |
#important |
0 | 1 | 0 | 0 | 0100 |
h1 + p::first-letter |
0 | 0 | 0 | 3 | 0003 |
li > a[href=*"en-US"] > .inline-warning |
0 | 0 | 2 | 2 | 0022 |
#important div > div > a:hover , inside a <style> element |
1 | 1 | 1 | 3 | 1113 |
注意: 如果多个选择器具有相同的重要性和特异性,则选择哪一个选择器获胜则取决于 Source order.
在我们继续之前,让我们看看一个行动中的例子。这是我们将要使用的HTML:
<div id="outer" class="container"> <div id="inner" class="container"> <ul> <li class="nav"><a href="#">One</a></li> <li class="nav"><a href="#">Two</a></li> </ul> </div> </div>
下面是CSS的示例:
/* specificity: 0101 */ #outer a { background-color: red; } /* specificity: 0201 */ #outer #inner a { background-color: blue; } /* specificity: 0104 */ #outer div ul li a { color: yellow; } /* specificity: 0113 */ #outer div ul .nav a { color: white; } /* specificity: 0024 */ div div li:nth-child(2) a:hover { border: 10px solid black; } /* specificity: 0023 */ div li:nth-child(2) a:hover { border: 10px dashed black; } /* specificity: 0033 */ div div .nav:nth-child(2) a:hover { border: 10px double black; } a { display: inline-block; line-height: 40px; font-size: 20px; text-decoration: none; text-align: center; width: 200px; margin-bottom: 10px; } ul { padding: 0; } li { list-style-type: none; }
我们从这个代码中得到的结果如下:
那么这里发生了什么事?首先,我们只对本示例的前七条规则感兴趣,您会注意到,我们在每个注释之前都包含了它们的特殊性值。
- 前两个选择器竞争链接的背景色的样式-第二个赢得和使背景色蓝色,因为它有一个额外的ID选择器在链中:其特异性为201比101。
- 第三个和第四个选择器在链接文本颜色的样式上进行竞争,第二个选择器获胜,使文本变白,因为缺少一个元素选择器,缺少的选择器被换成类选择器,它的值是十,而不是一个。所以获胜的特异性为113和104。
- 选择器5 - 7在挂起链接时对链接边框的样式进行竞争。选择器六明显地失去了五,其特殊性为23和24,它在链中有一个元素选择器。选择器七,但同时击败五和六-它有相同数量的子选择器在链中作为五,但一个元素已被换出了一个类选择器。所以获胜的特异性是33比23和24。
注意:如果还没有,请再次检查所有选择器,以确保理解特定值为什么被授予如图所示。
源代码次序
如上所述,如果多个相互竞争的选择器具有相同的重要性和特殊性,那么第三个因素将起到帮助决定哪一个规则获胜的目的,即以后的规则将战胜先前的规则。例如:
p { color: blue; } /* This rule will win over the first one */ p { color: red; }
而在这个例子中的第一个规则赢因为源秩序推翻的特异性:
/* This rule will win */ .footnote { color: blue; } p { color: red; }
关于规则混合的一点注记
当考虑到所有级联理论,以及应用于其他样式的样式时,应该记住的一点是,所有这些都发生在属性级别上——属性覆盖其他属性,但是没有覆盖其他规则的全部规则。当几个CSS规则匹配同一个元素时,它们都应用于该元素。只有在这之后,任何相互冲突的属性才会被评估,以确定哪种风格会战胜其他类型。.
让我们看一个例子。首先,一些HTML:
<p>I'm <strong>important</strong></p>
现在一些CSS风格与它:
/* specificity: 0002 */ p strong { background-color: khaki; color: green; } /* specificity: 0001 */ strong { text-decoration: underline; color: red; }
Result:
In this example, because of its specificity, the first rule's color
property overrides the color property of the second rule. However, both the background-color
from the first rule and the text-decoration
from the second rule are applied to the <strong>
element. You'll also notice that the text of that element is bolded: this comes from the browsers' default stylesheet.
继承
CSS继承是我们需要调查的最后一部分,以获取所有信息并了解什么样式应用于元素。其思想是,应用于某个元素的一些属性值将由该元素的子元素继承,而有些则不会。
- For example, it makes sense for
font-family
andcolor
to be inherited, as that makes it easy for you to set a site-wide base font by applying a font-family to the<html>
element; you can then override the fonts on individual elements where needed. It would be really annoying to have to set the base font separately on every element. - As another example, it makes sense for
margin
,padding
,border
, andbackground-image
to NOT be inherited. Imagine the styling/layout mess that would occur if you set these properties on a container element and had them inherited by every single child element, and then had to unset them all on each individual element!
默认情况下继承哪些属性,哪些属性不是普通意义上的。但是,如果你想确定,你可以查阅 CSS Reference — 每个单独的属性页都会从一个汇总表开始,其中包含有关该元素的各种详细信息,包括是否继承了该元素。
控制继承
CSS 提供了三个特殊值来处理继承:
inherit
: 该值将应用到选定元素的属性值设置为与其父元素一样。initial
:该值将应用到选定元素的属性值设置为与浏览器默认样式表中该元素设置的值一样。如果浏览器默认样式表中没有设置值,并且该属性是自然继承的,那么该属性值就被设置为inherit
。unset
:该值将属性重置为其自然值,即如果属性是自然继承的,那么它就表现得像inherit
,否则就是表现得像initial
。
The inherit
value is the most interesting — it allows us to explicitly make an element inherit a property value from its parent.
让我们看一个例子。首先是一些HTML:
<ul> <li>Default <a href="#">link</a> color</li> <li class="inherit">Inherit the <a href="#">link</a> color</li> <li class="initial">Reset the <a href="#">link</a> color</li> <li class="unset">Unset the <a href="#">link</a> color</li> </ul>
现在一些CSS样式:
body { color: green; } .inherit a { color: inherit; } .initial a { color: initial } .unset a { color: unset; }
Result:
让我们解释这里发生了什么:
- 我们首先设置
<body>
的color
为绿色。 - 由于
color
属性是自然继承的,所有的body子元素都会有相同的绿色。需要注意的是默认情况下浏览器设置链接的颜色为蓝色,而不是自然继承color属性,因此在我们列表中的第一个链接是蓝色的。 - 第二条规则是设置了链接在一个使用了类 inherit 来继承自父元素color的元素上。 在这种情况下, 意思是说链接继承了父元素
<li>
的颜色, 默认情况下li的颜色来自于它的父元素<ul>
, 最后ul的颜色继承自<body>
元素, 而body的color
根据第一条规则设置成了绿色。 - 第三个规则选择了在元素上使用类initial的任意链接然后设置他们的颜色为initial。通常,initial的值被浏览器设置成了黑色,因此该链接被设置成了黑色。
- 最后一个规则选择了在元素上使用类unset的所有链接然后设置它们的颜色为unset — 即我们不设置值。因为color属性是一个自然继承的属性,它实际上就像把值设置成inherit一样。结果是,该链接被设置成了与body一样的颜色 — 绿色。
主动学习: playing with the cascade
在这个活跃的学习中,我们希望您尝试编写一个新的规则,它将覆盖我们在默认情况下应用到链接的颜色和背景颜色。 Can you use one of the special values we looked at in the Controlling_inheritance 要在一个新规则中写一个声明,将背景颜色重置为白色,而不使用实际的颜色值?
如果你犯了一个错误,你可以用重置按钮重新设置它。如果你真的卡住了,按下显示解决方案按钮看一个潜在的答案。
Playable code
<div class="body-wrapper" style="font-family: 'Open Sans Light',Helvetica,Arial,sans-serif;"> <h2>HTML Input</h2> <textarea id="code" class="html-input" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;"><div id="outer" class="container"> <div id="inner" class="container"> <ul> <li class="nav"><a href="#">One</a></li> <li class="nav"><a href="#">Two</a></li> </ul> </div> </div></textarea> <h2>CSS Input</h2> <textarea id="code" class="css-input" style="width: 90%;height: 12em;padding: 10px;border: 1px solid #0095dd;">#outer div ul .nav a { background-color: blue; padding: 5px; display: inline-block; margin-bottom: 10px; } div div li a { color: yellow; }</textarea> <h2>Output</h2> <div class="output" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;"></div> <div class="controls"> <input id="reset" type="button" value="Reset" style="margin: 10px 10px 0 0;"> <input id="solution" type="button" value="Show solution" style="margin: 10px 0 0 10px;"> </div> </div>
var htmlInput = document.querySelector(".html-input"); var cssInput = document.querySelector(".css-input"); var reset = document.getElementById("reset"); var htmlCode = htmlInput.value; var cssCode = cssInput.value; var output = document.querySelector(".output"); var solution = document.getElementById("solution"); var styleElem = document.createElement('style'); var headElem = document.querySelector('head'); headElem.appendChild(styleElem); function drawOutput() { output.innerHTML = htmlInput.value; styleElem.textContent = cssInput.value; } reset.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = cssCode; drawOutput(); }); solution.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = '#outer #inner a {\n background-color: initial;\n color: red;\n}\n\n#outer div ul .nav a {\n background-color: blue;\n padding: 5px;\n display: inline-block;\n margin-bottom: 10px;\n}\n\ndiv div li a {\n color: yellow;\n}'; drawOutput(); }); htmlInput.addEventListener("input", drawOutput); cssInput.addEventListener("input", drawOutput); window.addEventListener("load", drawOutput);
下一步是什么
如果你理解了这篇文章的大部分内容,那么你已经开始熟悉CSS的基本原理了。最后一点的核心理论是盒子模型,我们将在下一章中讨论。
如果你没有完全理解级联、专一性和继承性,那就不用担心了!这绝对是我们迄今为止所涉及到的最复杂的事情,甚至是专业的Web开发人员有时也会发现棘手的问题。我们建议你在继续进行这门课的过程中几次回到这篇文章,并继续思考它。如果您开始遇到样式不适用的奇怪问题,请返回这里。它可能是一种特异性问题。