前言
本文共两个部分, 前面简要介绍CSS层叠特殊性的基本概念, 之后会探讨一些本人在项目中遇到的关于层叠的问题, 以及个人归纳的一些应用技巧.
关于层叠特殊性的详细文档可参考MDN (CSS层叠, Cascade and inheritance).
层叠特殊性基本概念
层叠
在样式表中, 同一个dom元素可能会有多条样式规则. CSS会通过一个层叠的处理过程, 为每一条规则分配一个重要度, 也就是特殊性, 并应用其中特殊性最高的规则.
特殊性
特殊性分类
选择器的特殊性分为四个等级,从高到低依次为:
- A级: 行内样式
- B级: id选择器总数
- C级: 类,伪类,属性选择器总数
- D级: 类型选择器,伪元素选择器总数
特殊性计算规则
层叠在计算特殊性高低时会首先从A级进行比较, 若相同则比较B级, 之后依次向下对比. 其中较大的规则, 其特殊性则越高.
需要特别说明的是, 若特殊性完全相等, 层叠会应用后定义的样式. 若使用CSS预编译工具, 编译后的结果中, 样式定义的顺序可能与预编译文档中顺序不一致, 这里需要特别注意.
计算示例
下表是特殊性计算的一些示例(示例中特殊性从高到低依次排列):
选择器 | 特殊性(A,B,C,D) |
---|---|
style=”” | 1,0,0,0 |
#wrapper #content {} | 0,2,0,0 |
#content .datePosted {} | 0,1,1,0 |
div #content {} | 0,1,0,1 |
#content {} | 0,1,0,0 |
p.comment .datePosted {} | 0,0,2,1 |
p.comment {} | 0,0,1,1 |
div p {} | 0,0,0,2 |
p {} | 0,0,0,1 |
</br>
层叠特殊性的应用技巧探讨
示例一: 区别一般样式与特殊样式,简化CSS
dom示例:
<div>
<div class="page">
<div class="news">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
<div class="archive">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
</div>
<div class="page">
<div class="news">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
<div class="archive">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
</div>
</div>
需求拟定:
- 所有post字体大小为14px
- 第一个page下,archive的第一个post为红色
样式表选择器可能是这样:
.post {
color: gray;
font-size: 14px;
}
.page:first-child .archive .post:first-child {
color: red;
}
此处问题在于, 第二个选择器太长, 不便于阅读与理解, 维护的时候可能无法通过该选择器第一时间找到相关dom.
此时可以尝试添加一个特殊的样式类post-red以便于区分一般样式和特殊样式:
// 一般样式
.post {
color: gray;
font-size: 14px;
}
// 特殊样式
.post-red {
color: red;
}
之后只需要在dom结构上添加对应class类名即可:
<div>
<div class="page">
<div class="news">
<div class="post post-red"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
<div class="archive">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
</div>
<div class="page">
<div class="news">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
<div class="archive">
<div class="post"><p>news content</p></div>
<div class="post"><p>news content</p></div>
</div>
</div>
</div>
这样处理的好处在于, 可以去掉之前样式表中复杂的多层选择器, 并且dom结构中, 能够明确表明哪一部分是经过了特殊处理, 更有利于后期维护.
</br>
示例二: 类名,ID名一般不使用与样式有关的名称,名称应体现其内容结构
依旧参照示例一中的dom结构与样式表, 而此时需求发生了更改, 需要将之前红色改为橘红色.
此时样式表可以由之前的
.post-red {
color: red;
}
改为
.post-orange {
color: orange;
}
而这里的问题在于class类名由post-red改为了post-orange, 意味着dom中被调用的位置也需要替换为新类名. 如果post-red被大量引用, 且这个关键词不止用于当前的类名, 可能需要我们手动筛选出项目中作为类名的post-red关键字, 并将其替换掉. 而项目中则应该避免这样耗时而重复的工作量.
上述示例中, 类名post-red更倾向于标明重点post, 但重点post不一定是红色, 所以使用post-point替代post-red可能更为合理.
</br>
示例三: 慎用层叠覆盖样式, 尤其慎用important覆盖样式
示例dom及样式表:
<div id="content">
<div id="main-content">
<h2>content 1</h2>
<p>content 2</p>
<div class="news-story">
<h2 class="first">content 3</h2>
<p>content 4</p>
</div>
</div>
</div>
#content div#main-content h2 {
color: gray;
}
#content #main-content > h2 {
color: blue;
}
body #content div[id="main-content"] h2 {
color: green;
}
#main-content div.news-story h2 {
color: orange;
}
#main-content [class="news-story"] {
color: yellow;
}
#main-content div.news-story h2 .first {
color: red;
}
此示例中, 应用于h2元素的选择器较多且较复杂, 所导致的问题是, 从样式表中去确认dom结构中具体h2的最终颜色将变得十分困难.
若此刻需求是需要content 1为红色, 可能会出现这样的样式解决问题:
#main-content div.news-story >h2 {
color: red !important;
}
然而, 这样的处理方式, 会导致样式表更加复杂. 当需求不断变更, 越发复杂的样式表会导致其更加依赖这样简单覆盖的处理方式, 形成恶性循环. 当important已不再有效时, 最终的结果可能是整个样式推翻重写.
样式变更, 应该是优先考虑修改, 慎用覆盖.
总结
层叠特殊性方便了样式表的构建, 但同样易被滥用. 在实现需求时, 我们可能会选择简单粗暴的方案, 然而却会导致后期维护成本剧增. 参考一些简单技巧, 可以让项目更利于长期的扩展与维护.