CSS 层叠 CSS3
了解 CSS 的同学都应该知道,CSS 是由Cascading Style Sheets三个单词首字母组成,简单的讲就是层叠样式表。事实上,在 CSS 中,层叠(也有同学称之为级联)也是一个非常基础,但也是一个非常重要的概念。只有理解了层叠这个概念才能更好的理解和使用 CSS 中的每一个属性。话又说回来,既然是一个基础概念,懂 CSS 的同学都知道,那还有必要来说这个吗?那我就得问一下了,你真的懂 CSS 中的层叠,能使用好 CSS 的层叠吗?如果你没有这方面的自信,那不仿花点时间来阅读这篇文章。
更多精彩内容请看 web 前端中文站
http://www.lisa33xiaoq.net 可按 Ctrl + D 进行收藏
管理 CSS 层叠的困惑
有些时候 CSS 层叠成为很多开发者(特别是不太熟悉 CSS 的开发人员)的眼中钉。为什么这么讲呢?在 CSS 中有些属性是有继承关系的(之前在当元素元素的祖先元素设置了样式),当我们想要恢复属性的默认值的时候,就必须记住该属性的默认值是什么?可想而之,如果不熟悉其默认值,又想覆盖继承过来的样式,那将是多么一件痛苦的事情。
所幸的是,CSS 提供了一些机制来处理继承。
理论上讲,任何 CSS 属性都具有除
none
之外的四个值:initial
、inhert
、unset
和revert
。这些值就是 CSS 用来处理继承的机制。
虽然这些值有些还没有普遍得到浏览器的支持,为了更好的管理 CSS 层叠,或者说处理好 CSS 的继承,我们还是很有必要的对他们进行了解。
回顾 CSS 的语法
CSS 的语法其实非常的简单,在 CSS 样式表中,我们都像下面这样使用 CSS:
比如:
html { font-size: small; }
使用规则是非常的简单,问题还是前面说的,有些属性是会被继承的,比如上面代码中的font-size
,它就是一个会被继承的属性,如果你的代码中在html
元素设置了font-size:small;
属性,那么html
元素的所有后代元素都将被继承这个属性,比如下图中蓝框中显示的一样:
上图蓝框中告诉我们body
元素继承了html
元素中的font-size:small;
。开发者工具中会提示我们“Inherited from html”。那么问题来了,在 CSS 中哪些属性是会被继承的呢?其实在 W3C 规范中各个属性的描述已清很清楚的告诉我们了。比如说border
属性,在描述其语法时,在列表中有一个Inherited描述项的值为no。这也就告诉我们border
属性是不能被继承的。反之,再来看一个font-size
属性,语法描述的列表中同样有一个Inherited描述项,只不过它的值不是no,而是yes,也就是说font-size
属性是会被继承的。这决定了当你没有为元素的属性指定值时该如何计算值。
如果你平时阅读规范仔细的话,你不难发现,在介绍每个属性的语法参数的时候,都会有一个Initial参数,该参数主要来指定每个属性的初始值。CSS 属性已经给出的初始值针对不同的继承和非继承属性有关不同的含义:
- 对于继承属性,初始值只能被用于没有指定值的根元素上
- 对于非继承属性,初始值可以被用于任意没有指定值的元素上
到目前为止,我们引出两个概念:初始值和继承值,除了这两个概念之外,在 CSS 属性中还有一个计算值(Computed),该值由指定的值计算而来:
- 处理特殊的值
inherit
和initial
- 根据属性的摘要中关于计算值描述的方法计算出值
计算属性的计算值通常包括将相对值转换成绝对值,比如em
和%
这样的单位。比如,有一个元素的属性值:
font-size: 16px; padding-top: 2em;
其中padding-top:2em;
就是一个计算值,其计算出来的值将根据font-size
做为基数计算(在此示例当中),在此计算出来的值是32px
。
然而,有些属性的百分比值会转换成百分比的计算值(这些元素的百分比相对于需要布局后才能知道的值,比如width
、margin-right
、text-indent
和top
等)。另外,line-height
属性值如果没有单位的数字,则该值就是计算值。
对于 CSS 的计算值,在不同的浏览器中其计算出来的值有时候会稍有偏差。
如果你感兴趣的话,可以打开浏览器的开发者工具,查看对应的计算值(比如,Chrome 开发者工具,有一个Computed选项,该选项展示的就是对应的 CSS 计算值),如下图所示:
其中计算值的最主要用处是继承,包括
inherit
关键词。
最后总结两点:
- 当元素的一个继承属性没有指定值时,则取父元素的同属性的计算值,只有文档根元素取该属性的概术中给定的初始值
- 当元素的一个非继承属性没有指定值时,则取属性的初始值
看到这里,或许你知道了什么叫继承和非继承,以及他们取值方式。但你可能还在纠结,在 CSS 中到底哪些属性是继承属性,哪些不是继承属性?其实这个问题我也没办法准确的回答您,因为我也没有做过这方面的统计。不过我可以告诉大家两个小经验:
- 在 CSS 中一些关于字体、文本和颜色等属性都是可继承属性
- 在 CSS 中一些跟布局和盒子模型的属性都是非继承属性
如果你想准确的知道答案,可以通过这里整理的属性表格进行统计。只要Inherited选项是Yes的都表示是继承属性,否则都是非继承属性。
处理 CSS 继承的机制
在文章开头也提到过,到今天为止,在 CSS 中提供了处理 CSS 继承的机制,简单的讲就是 CSS 提供了几个新属性,可以用来处理属性的继承。这几个属性就是initial
、inherit
、unset
和revert
。其实初了这四个属性之外,还有一个all
属性。虽然这几个属性主要是用来帮助大家处理 CSS 属性继承的,但他们之间的使用,还是有一定的差异化的。接下来我们一起来看看这几个属性的实际使用以及对应的差异化:
initial
在 CSS 中,每个属性都具有一个初始值,其实也就是 CSS 属性的默认值。在 CSS 规范中,都对每个属性的初始值做出了相关的定义。比如text-align
的初始值是left
,display
的初始值是inline
。
而这里,我们要说的是 CSS 的关键词initial:
If the cascaded value is the initial keyword, the property’s initial value becomes its specified value.
如果你在元素样式的设置中显示的设置某个属性的值为initial
时,其实就表示设置了该属性的默认值。这一点可能理解起来有点蛋疼,我们来看一个小示例。
假设我们有一个<p>
元素,从所周知,<p>
元素是一个块元素,为了好看,咱们添加一点修饰的样式代码:
p { background: #f36; padding: 2rem; font-size: 2rem; color: #fff; }
看到的效果将是这样:
如果我们希望p
元素变成行内元素时,按照我们以前的处理方式,需要手动处理浏览器默认样式(User Agent Stylesheet),也就是显示的重置:
p{ dispaly: inline; }
前面提到过inline
是display
的初始值(也就是默认值),而在规范中也提到过:你在元素样式的设置中显示的设置某个属性的值为initial
时,其实就表示设置了该属性的默认值。言外之意,我是不是可以直接使用initial
呢?尝试一下:
p { display: initial; }
这个时候得到的效果其实和使用display:inline
是一样的:
接下来,我们再来看一个继承属性color
。比如:
p { background: #ded; padding: 2rem; font-size: 2rem; color: #F36; margin-bottom: 3rem; }
因为color
是一个继承属性,所以结构中的strong
也将继承p
元素中设置的color:#f36;
的颜色。如果你在strong
中设置color: initial
时,那么strong
的颜色将重置为默认值。由于我们没有设置默认的color
颜色,那么这个时候,浏览器将会把一个计算值赋予成color
的初值始:
看到的效果就如下:
initial
的兼容性还是不错的,得到了大多数主流浏览器的支持:
Inherit
CSS 还添加了一个inherit
关键词属性值,用来强制继承父元素的某个属性的值。前面也说过,CSS 中有些属性自动就是可继承属性,比如font-size
、color
之类,但也有很多属性又是非继承属性,比如border
、border-radius
之类的。在这里,如果在非常继承的属性上显示的设置了inherit
关键词,表示该元素将继承父元素指定的属性值或者计算值。
为了同样的能更好理解inherit
,来看一个示例,在这个示你中,我们用border
来做例子:
<div class="wrapper" style="border:5px solid blue;"> <div>...</div> </div>
众所周知,border
是一个非继承属性,如果我们希望.wrapper
中的div
元素继承其父元素.wrapper
中的border
样式,以前的做法是,显式在div
中设置一个与.wrapper
一样的border
样式:
<div class="wrapper" style="border:5px solid blue;"> <div style="border: 5px solid blue;">...</div> </div>
其实有了inherit
关键词之后,一切变得是那么的简单:
<div class="wrapper" style="border:5px solid blue;"> <div style="border: inherit;">...</div> </div>
得到的效果将是一样的:
上面的示例是父元素设置了border
样式,所以其继承了父元素的border
样式。那如果将上面的示例稍做修改,在元素外套一个div
,而这个div
不做任何样式的设置。将又会变成一个什么样呢?直接上示例吧:
<div class="wrapper" style="border:5px solid blue;"> <div> <div style="border: inherit;" class="ele">...</div> </div> </div>
猜猜效果,是不是和你想的一样:
可以看到div.ele
仅继承了其父元素div
的border
属性的计算值,并未继承其祖先元素.wrapper
的border
属性的设置值,通过浏览器开发者工具,可以看得一目了然:
这个示例说明:仅管元素自身显式的设置了inherit
关键词,但是,如果其父元素没有明确指定样式,那么其最终效果将和revert
的效果一致。即继承的是其父元素的计算值,也就是浏览器默认样式(User Agent Stylesheet)。
Revert
revert
值早前被称之为default
。表示没有使用任何属性值。
我们都知道,如果没有使用作者样式表(也就是 Web 开发人员自己写的样式表),那么浏览器将会按这样的过程去检测,元素调用的样式:
- 浏览器首先会检测元素是否有使用作者样式表(User Defined Stylesheet)的属性
- 如果没有找到,将会检测客户端默认样式(User Agent Stylesheet)
- 如果没有找到客户端样式,相当于元素应用了
unset
还是拿示例来说吧。比如我们一个div
元素,我们并没有显示的在自己的样式表中设置其display
属性的值。对于最后的渲染结果,浏览器将会使用 User Agent Stylesheets 的样式display:block
。
根据前面介绍的,就算是我们在div
中显式的设置display:revert
,该元素也将使用 User Agent Stylesheet 中的display:block
样式。同样,我们在另一个div
元素中使用display:initial
,根据前面介绍的,那在这个div
将会采用display
的初始值inline
。比如下面的效果:
我们再来看一个继承属性的运用场景。因为在div
元素上设置了color:#fff;
元素,这是用户写的样式,而且color
是一个继承属性,只要是div
的后代元素都将会继承color
的属性值。根据前面所说revert
的检测机制是,先检测用户自己写的样式,然后再检测用户代理样式,如果两都没有,才会设置unset
的样式。所以最终我们看到的效果如下:(第二个设置了color:initial;
)
到目前为止,该属性值仅 Safari 浏览器支持:
Unset
unset
是initial
和inherit
的组合。当属性设置为unset
时,如果它是一个继承属性,那么它相当于是inherit
;如果它不是,则相当于initial
。
有一些属性,如果没有明确指定,将默认是inherit
。比如,我们给元素设置一个color
,那它将适用于所有默认的子元素。而其它属性,如border
则默认是非继承属性。
<div class="wrapper" style="border: 5px solid blue;color: #fff;"> <div class="ele">...</div> </div>
此时效果如下:
示例中color
属性被继承了,但border
属性没有被继承。
将上面的示例代码稍作调整:
<div class="wrapper" style="border: 5px solid blue;color: #fff;"> <div class="ele" style="border: unset; color:unset;">...</div> </div>
div.ele
元素的border
和color
都设置了unset
值。也就是说它将运用initial
或者inherit
的值。具体取决于哪个值,这得根据属性的默认行为是什么来决定。如果默认属性是inherit
,那将运用的是inherit
;如果默认属性是initial
,那将运用的是initial
。
上面的示例中,border
属性采用的是initial
,color
属性采用的是inherit
。
unset
关键词得到众多浏览器的兼容:
All
在 CSS 中,all
是一个简写属性,其重设除了unicode-bidi
和direction
之外的所有属性至它们的初始值或继承值。all
有三个值:
initial
:该关键字代表改变该元素或其父元素的所有属性至初始值。inherit
:该关键字代表改变该元素或其父元素的所有属性的值至他们的父元素属性的值。unset
:该关键字代表如果该元素的属性的值是可继承的,则改变该元素或该元素的父元素的所有属性的值为他们父元素的属性值,反之则改变为初始值。
来看示例。比如我们一个这样的一个 HTML 结构:
<div>...<strong>...</strong> ...</div>
给他们设置一些样式:
body { padding: 2vw; } div { background: #f36; padding: 2rem; font-size: 2rem; color: #fff; margin-bottom: 3rem; } strong { font-size: 3rem; }
看到的效果如下:
这效果正是我们想要的。div
显式的指定了background
、padding
、font-size
、color
和margin-bottom
属性值,而其中background
、padding
、margin-bottom
是非继承属性,而color
和font-size
是继承属性。除此之外,div
还有一个客户端代理样式display:block
,这个属性也是一个非继承属性。另外strong
元素设置了一继承属性font-size
,这个元素默认情况下继承了共父元素的color
属性,同时还继承了html
元素的font-family
和line-height
属性。当然,strong
元素也有一个客户端代理样式font-weight:bold
。
上面看到的效果是我们平时使用的时候效果。如果这个时候,我们在div
和strong
同时设置all:inherit;
时,得到的效果和前面的效果完全不一样:
这个时候div
和strong
重置了当初自己设置的属性,并且继承了各自父元素的一些属性:
div
元素继承了body
元素的padding
、font-family
、line-height
,同时也继承了body
代理客户端的样式color
、background
和display
strong
元素继承了div
元素的样式
所以最后你看到的效果就像上图那样子。我们再把all
的值设置为initial
:
这个时候div
和strong
样式都重置到了对应的初始样式,也就是对应的属性的默认样式,包括代理客户端样式也重置为对应属性的初始值。
最后来看all
的值设置为unset
的效果,下面的示例,我只在strong
元素上设置all:unset
,其效果就足以说明一切:
效果中的第一个是没设置all:unset
,第二个设置了all:unset
。这个时候strong
元素的font-size
和font-weight
继承了其父元素的font-size
和font-weight
。
all
在 CSS 中有时候是一个属性,比如这里说的就是属性,但有的时候它还是 CSS 中某些属性的值。比如我们常在transition
中用到的all
,那这个时候就是属性值。到目前为止,CSS 中的all
属性也得到了众多浏览器的支持。
总结
这篇文章主要介绍了 CSS 层叠管理。到目前为止,CSS 层叠管理可以通过inherit
、initial
、unset
和revert
四个值进行管理。简而言之:这些值就是 CSS 用来处理继承的机制。可以更好的管理 CSS 层叠,或者说处理好 CSS 的继承。
【注:本文源自网络文章资源,由站长整理发布】