前言:在之前做项目的过程中,有遇到因为CSS优先级导致页面页面上的样式无法生效的问题,所以打算写一篇文章记录下这块的知识。
选择器的匹配规则
之前的一篇文章中有提到过浏览器渲染的基本过程(链接),CSS选择器的匹配是发生在生成**渲染树(Render Tree)**的过程中。
- 浏览器为了节约性能,在解析CSS的过程中会从右往左读取CSS,这是因为防止从左往右读取的时候,会发生大量的回溯造成浏览器的性能损耗。
- 浏览器还会把不同类型的选择器(id、class、tag 及其他类型)归类到哈希表中,进一步减少查找基数。
了解选择器的匹配原理,有利于我们理解其权重规则,对于编写简洁、高效的 CSS 代码非常有帮助。
CSS权重
浏览器通过优先级来判断哪些属性值与一个元素最为相关,从而在该元素上应用这些属性值。
优先级是基于不同种类选择器组成的匹配规则。------MDN-优先级
白话:CSS权重又被称为CSS优先级,优先级高则浏览器使用该样式的的优先级就更高
权重的规则
优先级就是分配给指定的 CSS 声明的一个权重,它由 匹配的选择器中的 每一种选择器类型的 数值 决定。
而当优先级与多个 CSS 声明中任意一个声明的优先级相等的时候,CSS 中最后的那个声明将会被应用到元素上。
当同一个元素有多个声明的时候,优先级才会有意义。因为每一个直接作用于元素的 CSS 规则总是会接管/覆盖(take over)该元素从祖先元素继承而来的规则。
备注: 文档树中元素的接近度(Proximity of elements)对优先级没有影响。
------MDN-优先级
简单来说就是有优先级的计算遵循几个规则:
- 权重不同的样式作用于同一个元素的时候,权重高的样式优先生效
- 权重相同的样式作用于一个元素的时候,后声明的样式生效
- 选择器在DOM树中的位置关系不会对权重产生影响
- 继承的CSS 样式不如为目标元素直接添加的CSS 样式
- 在同一组属性设置中标有
!important
规则的优先级最大(不推荐使用此关键字)
案例1:权重无视 DOM 树中的距离
比如下面这段代码:
CSS:
css
body h1 {
color: green;
}
html h1 {
color: purple;
}
HTML:
html
<html>
<body>
<h1>Here is a title!</h1>
</body>
</html>
最后显示出来的样式:
上面这种情况body
标签距离h1
标签的距离更近但是最后显示出来的文字却是紫色的,这是因为此时二者的权重相同,而后声明的样式生效(规则2)
案例2:直接添加样式 vs. 继承样式
为目标元素直接添加样式,永远比继承样式的优先级高,无视优先级的遗传规则。
比如看下面这段代码:
CSS:
css
#parent {
color: green;
}
h1 {
color: purple;
}
HTML:
html
<html>
<body id="parent">
<h1>Here is a title!</h1>
</body>
</html>
最后显示出来的样式:
虽然div#parent
的优先级更高,但是这里的h1
是继承自div#parent
的color
属性,此时直接作用于h1
标签的标签选择器会比div#parent
的优先级更高。
CSS权重等级
一般情况下,CSS的权重等级有5种,按照由高到低的顺序来排列:
!important
关键字- 内联样式
- id选择器
- 类选择器、属性选择器、伪类选择器(
:where()
、:is()
与:not()
比较特殊) - 标签选择器、伪元素选择器
❕注意:
- 通配符选择器(
*
) 和 结合符(+
、>
、~
)对权重没有影响。 - 尽量不要使用
!important
,特别是**在全站范围的 css **以及制作对外发布的插件的时候在插件代码当中使用,因为它改变了你样式表本来的级联规则,从而使其难以调试。 - 减少不必要的选择器嵌套,嵌套最好不要超过三级。大量的复合选择器,会影响选择器匹配的效率,同时也会增加 CSS 样式文件的体积,不易维护。
- 当出现大量嵌套时,可以指定一个更具体的类选择器来替换复合选择器。
!important
关键字
当在一个样式声明中使用一个 !important 规则时,此声明将覆盖任何其他声明。虽然,从技术上讲,!important 与优先级无关,但它与最终的结果直接相关。使用 !important 是一个坏习惯,应该尽量避免,因为这破坏了样式表中的固有的级联规则 使得调试找 bug 变得更加困难了。当两条相互冲突的带有 !important 规则的声明被应用到相同的元素上时,拥有更大优先级的声明将会被采用。------MDN-优先级
非特殊情况下不要使用!important
关键字,遵循**AONN(Always Only Never Never)**的准则:
- Always 要优化考虑使用样式规则的优先级来解决问题而不是
!important
- Only 只在需要覆盖全站或外部 CSS(例如引用的 ExtJs 或者 YUI )的特定页面 中使用
!important
- Never 永远不要在全站范围的 CSS 上使用
!important
- Never 永远不要在你的插件中使用
!important
可以使用的情况:
-
在必要的情况下通过
!important
覆盖内联样式许多 JavaScript 框架和库都添加了内联样式。有时候可以用
!important
与优先级高的选择器一起使用,以重写覆盖这些内联样式。 -
覆盖优先级高的选择器
下面这种情况下,如果不使用
!important
,第一条规则永远比第二条的优先级更高css#someElement p { color: blue; } p.awesome { color: red; }
如何覆盖!important
:
-
添加一个优先级更高的样式
再添加一条 带 !important 的 CSS 规则,再给这个给选择器更高的优先级(添加一个标签,ID 或类)··
csstable td { height: 50px !important; } .myTable td { height: 50px !important; } #myTable td { height: 50px !important; }
-
使用相同的选择器,置于已有的样式后
csstd { height: 50px !important; }
-
重写原来的样式,避免使用
!important
将 id 作为属性选择器的一部分而不是 id 选择器,下面的两个选择器现在具有相同的权重。在优先级相同情况下,后面定义的 CSS 样式会被应用。
css[id="someElement"] p { color: blue; } p.awesome { color: red; }
内联样式
语法:
html
<div style="color: red;">测试</div>
id 选择器
语法
css
#test {
color: red;
}
类选择器、属性选择器、伪类选择器
语法:
css
.demo {}
[type="text"] {}
div:hover {}
div:first-child {}
:where()
、:is()
与:not()
-
:not()
与:is()
:在计算权重的时候不会被视作伪类,而是将参数中的选择器作为判定权重的标准参考:链接
-
:where()
:该伪类的权重始终为0 -
补充:
:is()
与:where()
的区别在于:is()
它以权重最高参数的也就是选择器判定权重,而:where()
的权重为0参考:链接
标签选择器、伪元素选择器
css
div {}
div:before {}
div:after {}
权重的计算方法
当遇到比较复杂的选择器的时候,需要做的是逐个比较每个选择器的权重大小,然后计算出来。
比如,现在有一个权重表格:
!important 关键字 |
内联样式 | id选择器 | 类、伪类、属性 | 标签、伪元素 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
现在有这样一个CSS
css
#testDiv li.class a[href]{}
问:该CSS的权重是多少?
解:
- 存在一个
id
选择器,**[id
选择器]**权重+1 - 存在类选择器、属性选择器,**[类、伪类、属性]**权重+2
- 存在两个标签标签选择器,**[标签、伪元素]**权重+2
答:
!important 关键字 |
内联样式 | id选择器 | 类、伪类、属性 | 标签、伪元素 |
---|---|---|---|---|
0 | 0 | 1 | 2 | 2 |
该属性的权重为0-0-1-2-2
。
实际上对于CSS样式权重,还可以参考下面这张图理解,权重的计算过程与上面的案例是一样的: