深入解析CSS样式优先级

665

作为前端多多少少都会对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只能添加一个,编写不同的类名来控制不同的样式显示,同时根据权重来控制样式的覆盖。

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

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

* {
  margin: 0;
  padding: 0;
}

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

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

主要选择器的权重比较

权重累加

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

<div id="box" class="box div"></div>
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代码与样式

<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>
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
  • 子选择器>,相邻兄弟选择器+等等

伪类的优先级(:)

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

<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 的一盒伪类。所以判定,伪类的权重与类的权重是相同的

属性选择器的优先级

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

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"]。同时顺序发生了改变。

代码可看[codepen.io/Anthony-Wil…]codepen.io/Anthony-Wil…

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

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

伪元素选择器(::)

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

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

<!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命名规范。