深入解析CSS样式优先级

作为前端多多少少都会对CSS样式的权重有一定的了解。最常用的方法就是对不同的选择器分配不同的权重比,常见的就是

选择器 权重值
!important标识 10000
行内样式 1000
id选择器 100
类选择器 10
标签选择器 1
通配符 * 0

具体的判断我们可以用一个矩阵来表示:(0, 0, 0, 0, 0)。那这里面的每一个矩阵的0表示的是

  • 第一个:!important 的个数
  • 第二个:行内的个数
  • 第三个:id选择器的个数
  • 第四个:类选择器的个数
  • 第五个:标签选择器的个数

行内除了!important高于其他的样式。同时,这里的这个矩阵实际上是不存在的,是认为的构建出来的。因为!important始终最高,所以这里可以忽略第一个,最后矩阵为(0, 0, 0, 0)。

上面表格中是我们在写css样式的时候需要了解最的基本的知识,也是最为普通的选择器权重分配。权重值越高,权重越高,那么样式的优先级就越高。所以,!important的权重是最高的。正式因为这一点,所以!important在编写css样式的时候一般都不建议使用,因为使用了!important后,要想在修改样式,几乎是不可能的,即,样式会变得不好控制。

其次是行内样式。在现在前端开发中,为了更好的阅读代码,是代码调理更清晰,一般都会把CSS代码以及JS代码从HTML中分离,采用外链的方式引入CSS以及JS。所以,行内样式在一般的开发中,用的也不多。但是,在使用前端三大框架或者是需要使用JS来控制样式的时候,多多少少的都会涉及到把样式写在HTML元素行内的情况。

接着是ID选择器。在编写样式的时候,我们一般都不会使用ID选择器来控制样式,同时也不会在HTML文档中添加过多的ID选择器。ID选择器一般更多的是用于获取元素,而不是用来控制CSS样式。

再者是类选择器。这个在CSS样式的编写中用的算是最多的一种,因为一个标签可以添加多个类名,不像ID只能添加一个,编写不同的类名来控制不同的样式显示,同时根据权重来控制样式的覆盖。

然后是标签选择器,这个在开发中也是不建议使用,更多的是建议添加一个类名来控制,以实现复用,同时方便控制。

最后是通配符选择器,这个选择器的使用一般就是初始化文档结构。例如

1
2
3
4
* {
margin: 0;
padding: 0;
}

但是更多的公司采取的是具有针对的样式重置,比如reset.css

以上就是常用的样式选择器以及他们的权重。

主要选择器的权重比较

权重累加

上面的权重值我们已经知道了,那么具体怎么来算呢?个人认为,需要记住一这点就OK了。相同类型的权重值累加,然后在比较相同类型选择器的值。举个🌰

1
<div id="box" class="box div"></div>
1
2
3
4
5
6
7
8
9
10
div {
width: 100px;
height: 100px;
}
#div {
background: green;
}
div.div {
background: red;
}

上面的代码中,来看看这个div盒子的颜色应该是什么呢?分析一下CSS样式:

根据上面的矩阵(0, 0, 0, 0)来分析。

#div的矩阵为(0, 0, 1, 0, 0),最后的权重值为:1*100 = 100。

div.div的矩阵为(0, 0, 0, 1, 1),最后的权重值为:1 10 + 1 1 = 11。

所以最后的样式显示 background: green;

有了这样的结论,我这里又做了一个测试。有下面的一段html代码与样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="box">
<div class="box1">
<div class="box2">
<div class="box3">
<div class="box4">
<div class="box5">
<div class="box6">
<div class="box7">
<div class="box8">
<div class="box9">
<div id="box10" class="box10"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
div {
width: 100px;
height: 100px;
}
#box10 {
background: green;
}
.box .box1 .box2 .box3 .box4 .box5 .box6 .box7 .box8 .box9 .box10 {
background: red;
}

按理说 #box10 的权重为 100 * 1 = 100

.box .box1 .box2 .box3 .box4 .box5 .box6 .box7 .box8 .box9 .box10 的权重为 10 * 11 = 110

下面使用多个类的权重值理论上是高于上面只使用一个id选择器的情况,但是最后的结构却不是我们想象的那样。结果还是 green 。没错,后面的11个类的样式无效。经过这样的测试,我们可以猜想,在一个元素使用了ID选择器修饰了样式以后,如果在使用类选择器,这时候是是没有办法使相同的样式属性生效,生效的依然是那个ID选择器修饰的样式。为什么权重值大也没有用呢?我猜或许是因为写了太多类名的时候再和ID相比的话,浏览器会自己去判断,选择最优的那个,毕竟10多个类名在实际的开发中是不存在的。在张鑫旭大神的有趣:256个class选择器可以干掉1个id选择器有一个实验,但是这个实验室在2012年的时候,那个时候,还是使用的IE浏览器能够呈现出来。但是现在大部分的浏览器都不能够呈现出256个class干掉一个id的情况了。同时,也证明了,上面我们定义的矩阵(0, 0, 0, 0)其实并不严谨,id与class之前的差距我们这里以10作为一个标准,但是实际上可能达到100,或者1000。即1000个class干掉一个ID。但是由于现代编码一般要求class的层级书写的时候不要超过四层,所以,目前这个问题也就没有什么意义了。

因此,上面的矩阵也可以作为我们判断的标准。

其他类型选择器的权重比较

上面说了几种常用的选择器了,但是还有一些选择器也是在开发中会出现,但是不是太常用的一些选择器。那么,有哪些呢?

w3c中样式选择器的权重优先级的排序如下

important > 内嵌样式 > ID > 类 | 伪类 | 属性选择 > 标签 | 伪元素 > 伪对象 > 继承 > 通配符 | 子选择器 | 相邻选择器

  • 伪类选择器,如:hover
  • 属性选择器,如[type="text"]
  • 伪元素选择器,如::first-letter
  • 子选择器>,相邻兄弟选择器+等等

伪类的优先级(:)

首先来看看伪类选择器的优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
margin-top: 10px;
height: 100px;
width: 100px;
}
.box1 {
background: lime;
}
:hover {
background: pink;
}
.box2 {
background: red;
}
</style>
</head>

<body>
<div id="box" class="box1"></div>
<div id="box" class="box2"></div>
</body>

上面的代码在codePen中的具体效果,可以看到

伪类与类的优先级

当将鼠标分别移动到两个div盒子上面的时候,前面的绿色盒子的背景色会发生变化,而红色盒子不会。但是,都是 :hover 的一盒伪类。所以判定,伪类的权重与类的权重是相同的

属性选择器的优先级

同样是上面的代码,我们把样式改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
div {
margin-top: 10px;
height: 100px;
width: 100px;
}
.box1 {
background: purple;
}
[class="box1"] {
background: lime;
}
:hover {
background: pink;
}
[class="box2"] {
background: red;
}
.box2 {
background: purple;
}

唯一不同的就是样式中添加了属性选择器[class="box1"][class="box2"]。同时顺序发生了改变。

代码可看[https://codepen.io/Anthony-Wilson/pen/YzKWZpL]https://codepen.io/Anthony-Wilson/pen/YzKWZpL

伪类,类与属性选择器的优先级

所以,属性选择的权重 = 类的权重 = 伪类的权重。三者是相等的,都是(0, 0, 1, 0);

伪元素选择器(::)

伪元素作为一种特殊的存在,我认为它不应该放在优先级里面同其他的选择器相比。比如 ::before::after 这两种伪元素都是在文档中添加一个假的元素,并不能够设置 id class 等属性。所以这里可以把它作为一个唯一的存在。那么他的权重我们可以看为1。同时没有和他比较的。

子选择器>,相邻兄弟选择器+

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box1 span {
color: yellow;
}
.box > span {
color: blue;
}
.box2 span {
color: red;
}
</style>
</head>

<body>
<div class="box box1"><span></span>是一个盒子</div>
<div class="box box2"><span></span>是一个盒子</div>
</body>

</html>

上面的例子在codePen中的运行结果

运行结果

可以看到,第一个我是蓝色,第二个我是红色。结合上面的代码,可以看出来子元素选择器和普通的空格其实没有太多的区别,同理兄弟选择器其实也是一样的。遇到这种情况,直接比较 class 与 标签的个数就可以了。即同基本的权重判断是相同的。

结果总结

经过上面的推想测试,可以大致的得出一个优先级的结论:

!important > ID > class = 属性 = 伪类 > 标签 > 通配符 > 继承 > 浏览器自带属性

在使用选择器的时候尽可能的选择使用 class选择器或者属性选择器(针对于input这一类)来对元素设置样式。一个是使用class可以复用,第二个可以更好的控制元素样式。同时,关于class命名的规范建议使用BEM命名规范。

补充:2019年08月22日

在网上看到这样的一道题

问题:已知如下代码,如何修改才能让图片宽度为 300px ?注意下面代码不可修改。

<img src=”1.jpg” style=”width:480px!important;”>

期初看到这段代码一下子就想到这都 !important 了还有办法限制它的宽度?真的是被无知限制了想象力。

这里提供几种方法实现修改宽度:

1、使用max-width

虽然使用了 important ,但是也只是添加到了 width 属性上面,并不影响 max-width 。所以这里设置了 max-width 即可生效。

1
2
3
img {
max-width: 300px;
}

2、使用 transform:scale

1
2
3
img {
transform: scaleX(0.625);
}

3、使用 zoom

这个和CSS3的缩放是一样的效果

1
2
3
img {
zoom: 0.625
}

4、使用 js

这个和CSS3的缩放是一样的效果

1
document.getElementsByTagName("img")[0].setAttribute("style","width:300px!important;")

5、使用弹性盒模型

这个和CSS3的缩放是一样的效果

1
2
3
4
img {
box-sizing: border-box;
padding: 0 90px;
}

6、使用animation

1
2
3
4
5
6
7
8
9
10
11
img {
animation: width 0s forwards;
}
@keyframes width {
from {
width: 300px;
}
to {
width: 300px;
}
}
文章作者: 踏浪
文章链接: https://blog.lyt007.cn/技术/深入解析CSS样式优先级.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 踏浪 - 前端技术分享
支付宝
微信打赏