CSS的进入门槛非常低,主要是由于其清晰和易于理解语法,即使你没有任何CSS经验,也可以在短短几个小时内学习一个简单的基于CSS的网站。
但是,表面上的简单性是骗人的。你花了几个小时制作了一个网页,可能在Safari上看起来很棒,但是你并没有去适配Internet Explorer,那么有可能会让你非常的崩溃。兼容性问题是CSS的遇到的最多的问题。要解决兼容性问题,真的需要积累很多的经验,依赖正确的css使用方式。
要想能够正确的使用CSS,精通CSS,理解一些概念是非常有必要的。
Specificity和Inheritance是这些概念中最重要的两个。
“级联”的概念是CSS的核心。它最终决定哪些属性如何应用到特定元素。级联有三个主要概念:importance、specificity和source order。这三者共同确定了要分配给元素的属性的权重。
浏览器通过优先级来判断哪一些属性值与一个元素最为相关,从而在该元素上应用这些属性值。优先级是基于不同种类选择器组成的匹配规则。
importance、specificity和source order共同决定了css的优先级。
importance (重要性)
样式有如下的来源:
用户代理:比如浏览器默认的样式.
用户:比如用户浏览器自己的设定的样式
作者(开发者):页面提供的CSS,可能是内嵌样式,可能是引用样式。
上面的顺序,也是浏览器处理不同来源样式的顺序,后面的优先级更高。
!important 在样式中需要考虑的一个重要声明。这个声明用于平衡用户和作者样式之间的优先级。虽然作者的样式优先级比用户高,但是,如果用户的样式规则里面有 !important,即使作者的规则里面有!important 也会被用户的规则覆盖掉。
知道了这些规则后,我们来看下最终的优先级顺序,下面是按照优先级升序排列:
用户代理声明的
用户声明的
作者声明的
作者使用!important的
用户使用!important声明的
Specificity(具性)
我个人翻译成了具性,个人理解的是更具体更细节的指定某个元素。
前面说过每个CSS规则都一个权重值,这个权重决定了在应用css属性遇到冲突的时候哪个规则生效。
在评估一个规则的权重时,一个更具体的规则比一个更大范围的规则优先级更高。
如果两个样式具有相同的权重、来源和具性,则只最后出现的规则生效。
2.1 如何计算具性
有一系列方式计算选择器的具性。
最快的计算方式如下:
元素和伪元素(像:before,:after)增加1;
属性(比如[type="text"]),类,伪类(:link 或者 :hover)增加10;
ID增加100;
内联样式增加1000;
用上面的方式计算下下面的选择器的具性:
p.note 1 类 + 1 元素 = 11
#sidebar p[lang="en"] 1 ID + 1 属性 + 1 元素 = 111
body #main .post ul li:last-child 1 ID + 1 类 + 1 伪类 + 3 元素 = 123
W3C的标准中也有一个类似的计算方式,以a=0, b=0, c=0, d=0为初始值,以如下情况换为数字:
如果是内联样式:a = 1;
b = ID的个数;
c = 选择器,类和伪类的个数和;
d = 元素名称的个数+ 伪元素的个数
根据上述算法,让我们看一个计算具性值的例子:
<p style="color:#000000;"> a=1, b=0, c=0, d=0 → 1000
footer nav li:last-child a=0, b=0, c=1, d=3 → 0013
#sidebar input:not([type="submit"]) a=0, b=1, c=1, d=1 → 0111
:not 否定伪类在优先级计算中不会被看作是伪类. 事实上, 在计算选择器数量时还是会把其中的选择器当做普通选择器进行计数.
如果使用@import引用CSS,引用的CSS要早于你的CSS,如果遇到两个选择器有相同的具性值,那么后面的将覆盖前面的。
2.2 让具性值为你工作
如果没有考虑清楚具性值是什么的话,可能会导致你的样式表既冗余又复杂。
遵循下面的准则可以避免大部分问题:
使用普通的选择器,逐渐的增加具性值;
使用高级选择器并不代表使用复杂的选择器;
更多的依赖具性值,而不是依赖选择器的顺序,这样的样式表更容易修改和维护;
重构CSS选择器要比简单的修改CSS属性要简单的多。
3.继承
继承是将属性值从父元素传播到其子元素的一种方式。
默认情况下,某些CSS属性由元素的子元素继承。例如,如果将body设置为特定字体,则该字体将由其他元素继承,如标题和段落,而无需特别指定。这就是继承的魔力。
CSS规范指定每个属性是否默认继承。不是所有的属性都是继承的,但是可以通过inherit强制继承它们。
3.1面向对象程序的继承
尽管超出了本文的范围,CSS继承不应与面向对象编程(OOP)继承相混淆。这里是维基百科OOP继承的定义,它清楚地说明我们说的是不同的事情:
在面向对象编程(OOP)中,继承是使用已经定义的类来形成新类(…)的一种方式。继承被用来帮助重用现有代码。新类(…)继承已存在的类的属性和行为。…
3.2 如何继承功能
当一个元素从它的父节点继承一个值时,它继承的是计算值。这意味着什么?每个CSS属性在确定其值时经历四步。以下是W3C规范的摘录:
属性的最终值要经历四步计算:
通过规范指定值(指定值),然后再被解析为用于继承的值(计算值),然后如果有必要的话在转换为绝对值(使用值),最后根据本地环境转换为实际值(实际值)
如下所示:
指定值:用户代理决定了属性值是来自样式表,继承的,还是初始值
计算值:指定值会被解析为计算值。计算值不依赖文档是否已平铺完。
使用值:文档平铺完成之后,由计算值转换为使用值。
实际值:最终用于渲染的值,把使用值转换为真正被渲染是使用的值。
如果你看CSS属性规范的时候,你会看到,它定义的初始值,继承状态,和计算值。比如background-color规范规范如下:
Name: background-color
Value: <color>
Initial: transparent
Applies to: all elements
Inherited: no
Percentages: N/A
Media: visual Computed
value: the computed color(s)
一个元素从其父元素继承的是计算值。即使在样式表中没有指定,属性值也可以被继承:此时继承的是初始值。所以即使父元素没有指定属性值也可以继承。
本文翻译并参考了如下两篇文章:
CSS Specificity And Inheritance
CSS优先级