CSS优先级顺序详细分析

HJ

S

TM• Style•C

LS

S

在HTML中,每个合法的文档都会生成一个结构树,根据元素的祖先、子孙、属性、兄弟元素等使用选择器,创建丰富的CSS规则,使其展现的更加美观。由于可以对选择器和规则分组,这使得样式表相当简洁,而且非常灵活,相应地可以缩小文件的大小,提升加载速度。

有了文档树,选择器才能生效,这是CSS继承的核心。继承(Inheritance)是从一个元素向其后代元素传递属性值所采用的机制。确定相应元素要应用哪些属性时,用户代理不仅需要考虑继承,同时需要考虑声明属性来源、声明顺序、权重和特殊性,这个过程称为层叠(cascade)。

那么,在错综复杂的规则匹配中,如何才能保证我们让别人看到的样子,就是我们想让别人看到的样子呢?我们需要考虑的主要影响因素为特殊性、声明来源和声明顺序的相互关联和影响。

▣1▣

特殊性

同一个元素可以使用两个或多个规则来选择,每个规则都有其自己的选择器,每一对规则中只有一个规则生效,那么哪个规则会胜出呢?

别急,答案就在每个选择器规则的特殊性(specificity),对于每个规则,用户代理会计算选择器的特殊性,并将这个特殊性附加到规则的各个声明。如果一个元素有两个或多个冲突的属性声明,那么最高特殊性的声明总会胜出。

特殊性规则总结起来有以下六点:

重要性声明!important特殊性优先于所有其他特殊性。

对于元素中给定的内联样式,特殊性加 1,0,0,0。

(栗子:style="")

对于选择器中给定的ID属性值,特殊性加 0,1,0,0。

(栗子:#eleId{})

对于选择器中给定的类属性值、属性选择或伪类,特殊性加 0,0,1,0。

(栗子:.css,*[name="n"],*:hover{})

对于选择器中给定的元素或伪元素,特殊性加 0,0,0,1。

(栗子:div,*:before{})

结合符和通配选择器,特殊性加 0,0,0,0。

(栗子:*{}, ' ', '+', '>', '~')

优先顺序比较起来,特殊性'0,0,1,0' 优先于特殊性'0,0,0,99',其他情况以此类推。特殊情况,'0,0,0,0'优先于无特殊性(比如继承)样式。

假设创建如下Dom树,并定义样式声明。

此处,共为"Let's see it."定义了9个匹配的选择器样式声明和一个内联样式声明,对比属性为字体颜色color,"Let's see it."在页面展示结果如下图

貌似也看不出来是什么颜色,我们来利用Chrome的Styles解析结果来分析样式声明规则应用顺序,浏览器解析结果规则顺序如下图

我们可以发现,最终应用的样式在匹配的选择器中排序第九,仅是凭借!important声明获得应用机会。我们对此十条选择器进行特殊性计算如下

(0,0,3,0) => .warp-3 .warp-4 .warp-5{}

(0,0,3,0) => .warp-3>.warp-4>.warp-5{}

(0,0,2,2) => div[id="w3"] div[id="w5"]{}

(0,0,0,8) => html body div div div div div div{}

(0,1,0,0) => #w5{}

(0,1,1,0) => #c1 .warp-5{}

(0,0,2,2) => div.warp-1 div.warp-5{}

(0,0,0,0) => *>*>*>*>*>*>*>*{}

(0,0,0,1) => div{}

(1,0,0,0) => style{}

根据特殊性规则优先级顺序如下

(1,0,0,0) => style{}

(0,1,1,0) => #c1 .warp-5{}

(0,1,0,0) => #w5{}

(0,0,3,0) => .warp-3>.warp-4>.warp-5{/**/}

(0,0,3,0) => .warp-3 .warp-4 .warp-5{}

(0,0,2,2) => div.warp-1 div.warp-5{/**/}

(0,0,2,2) => div[id="w3"] div[id="w5"]{}

(0,0,0,8) => html body div div div div div div{}

(0,0,0,1) => div{}

(0,0,0,0) => *>*>*>*>*>*>*>*{}

.warp-3>.warp-4>.warp-5{} 和 div.warp-1 div.warp-5{}两条匹配器规则由于靠后声明的原因(后续第三条会提及声明顺序优先级)优先于其他特殊性相同的匹配器规则。

我们将此结果与浏览器解析顺序对比发现,优先级顺序完全相同,只是浏览器在此十条属性只有追加了浏览器代理样式声明以及继承样式。也由此可以看出,浏览器代理样式声明和继承样式的优先级相对更低。

▣2▣

声明来源

如果特殊性相等的两个规则同时应用到同一个元素会怎样?浏览器如何解决这个冲突?声明来源或许可以解答。

首先介绍三个名词:

用户代理样式声明,是指浏览器的默认样式。

创作人员样式声明,是指前端开发者所定义的样式。

读者样式声明,是指浏览网页的用户不满足于网页的呈现样式时,通过浏览器提供的接口修改网页呈现而添加的样式。

一般来说,创作人员样式声明优先于读者样式声明,读者样式声明优先于用户代理样式声明。特殊情况下,读者的重要性(!important)声明优先于创作人员的重要性(!important)声明。总结起来,声明来源优先级顺序为: 读者重要性声明 > 创作人员重要声明 > 创作人员正常声明 > 读者正常声明 > 用户代理声明。

这个顺序应该也是比较容易理解,创作人员具有较高的优先性,但最终选择权依然只属于用户,用户代理只是用来补刀而已。

假设创建如下Dom树,并定义样式声明。

然后,我们修改下浏览器从而配合使用读者模式。尴尬的是,我们常用的Chrome浏览器貌似并不支持我们来做这一步的修改。首先,尝试了在形如C:\Users\xe\AppData\Local\Google\Chrome\User Data\Default\User StyleSheets的文件夹下面插入Custom.css来写入自定义CSS,扑街......再次尝试,引入插件Stylish(so华丽的介绍,so强大的功能),然而实际情况却是在body元素结束之后引入一个style标签,引入我们想要修改的样式,虽然这种方式确实可以设置为用户自定义样式,然而机智的我们发现,这种方式根本就是通过靠后声明顺序来覆盖了之前的声明而实现的,完全不是读者模式,扑街......那不行就自己封插件呗,参考下封装方法,打包好,安装,完,不是商店下载的没有证书,无法使用......扑...等下再街,那给开个白名单试试呗,度娘度娘我爱你...修改完系统配置,添加白名单,喜滋滋的一试,扑街......

还是算了,试了下IE。然后发现so easy就成功了,虽然是期望的结果,只是这显示也是太敷衍了。度娘度娘我爱你...终于找到一个Firefox配置方法,一次成功,完美。下图即为在Firefox浏览器中添加的读者模式样式声明。

在此,我们只对比color,font-size,font-family,

font-weight四个样式应用情况,并分配三种important权重属性。

通过浏览器中打开,"Let's see it." 页面展示如下图

我们关心的文字属性结果为:32px红色加粗Times New Roman字体,咳咳...主要是从下图看出来的。

Filefox浏览器对改元素匹配规则顺序解析如下图

其中,来源为userContent.css的属性声明即为此次修改的读者模式属性声明。首先,我们可以发现即使这条规则的特殊性(0,1,1,0)比页面head定义的input特殊性(0,0,0,1)及.mode特殊性(0,0,1,0)要高,但是它的优先级依然更低,这印证了之前所说的创作人员声明优先级高于读者声明优先级。其次,对比color属性声明可以发现,读者声明添加important权重时,优先级高于创作人员正常声明。再然后,对比同时添加important权重的font-size属性声明时,我们发现创作人员重要声明优先级高于读者重要声明。纳尼?WTF?明明说好的读者重要声明优先级最高呢?仔细一看,我勒个去,我们实际的字号的确是32px(详见上上图),这只是个显示BUG而已,莫慌,读者重要声明还是优先级最高的。然而这个时候,细心的小伙伴们或许又发现了其他显示BUGs,没错,是BUGs。在上上图中,最终计算结果和展示结果都没有错,在font-family属性和font-weight属性计算中,显示忽略了的属性却是我们最终的结果,就好像高中老师训我们的时候,“你拿一个错的东西忽悠出来一个正确的结果,还说你不是瞎蒙的”。就这样,我们成功测试出了Firefox开发者工具的显示BUGs......完...美...

▣3▣

声明顺序

当两个选择器规则的权重、来源和特殊性完全相同,又会是哪个规则会胜出呢?声明顺序决定了呈现样式。

如果两个规则的权重、来源和特殊性完全相同,那么在样式表中越靠后出现的规则优先级越高。

假设创建如下Dom树,并引入样式声明。

在浏览器中打开,"Let's see it." 页面展示如下:

浏览器对此元素所有匹配样式声明解析结果如下:

从浏览器解析结果顺序可以看出,优先级顺序从高到低依次为:内联样式、外链样式文件css3.css中声明的样式、外链样式文件css2.css中声明的样式、外链样式文件css1.css中声明的样式、head中声明的样式content2、head中声明的样式content、head中声明的div元素选择器样式、用户代理的div元素选择器样式、从body继承的样式声明。

其中,第二条至第六条匹配规则特殊性相同,权重相同,声明来源也相同(同为创作人员声明),所以在优先级上按照定义顺序排序,越后声明的规则优先局越高。从文档树中声明及引入声明顺序和浏览器解析结果对比中,可以发现完全印证这一点。另外,也可以发现,规则匹配中,只有冲突的属性声明(color)存在覆盖的情况,无冲突的属性(font-size,font-family,font-weight)的规则即使在优先级上没有胜出,但是这些属性声明依然能够成功应用到元素上。

由此,对于a标签伪类声明,“专家们”推荐使用link-visited-hover-active(LVHA)声明顺序

假设你想忽略这种常用的顺序,而是按字母顺序排列链接样式,即:

按照这种顺序,任何链接都不会显示:hover和:active样式,因为:link和:visited规则后出现,所有链接要么是已访问样式,要么就是未访问样式。

▣4▣

总结一下

通过以上的分析,我们可以稍微整理一下。所谓的层叠样式表(CSS)的优先级计算所基于的方法,就是让样式层叠在一起,然后通过特殊性、声明来源和声明顺序来决定的。总结起来,如下:

找出所有相关规则,这个规则都包含与一个给定元素匹配的选择器。

按显示权重对应到该元素的所有声明,标志important权重的规则优先级高于没有important权重的规则。

按声明来源对应到该元素的所有声明,读者重要声明 > 创作人员声明 > 读者正常声明 > 用户代理声明。

按特殊性对应到该元素的所有声明,特殊性高的属性声明优先于特殊性低的属性声明。

按属性声明顺序对应到该元素的所有声明,属性越是再文档中靠后声明,优先级越高。

▣5▣

参考文献

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181024G22W7P00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券