CSS中的Cascade——层叠

CSS本质上就是声明规则,即在各种条件下,我们希望产生特定的效果。CSS开发很重要的一点就是以可预测的方式书写规则。但是当多条规则提供了冲突的样式时会发生什么呢?

一、层叠

层叠Cascade指的就是这一系列规则。它决定了如何解决冲突,是CSS语言的基础,让代码能以可预测的方式产生特定效果。
当声明冲突时,会依据三种条件解决冲突:

  • (1) 样式表的来源:样式是从哪里来的,外部文件、内联代码、行内样式、浏览器默认样式或者用户样式;
  • (2) 选择器优先级:哪些选择器比另一些选择器更重要。
  • (3) 代码顺序:规则的声明顺序。

534de323327741f9b630a665474ee5fe.jpg

 声明遇到冲突:先考虑来源,其次考虑优先级,最后考虑源码顺序

  1. 来源:!important(最高级的来源样式) 优先于 作者样式(自己写的样式); 作者样式 优先于 用户代理样式(浏览器默认样式);
  2. 优先级: 先考虑是否行内样式,若不是行内样式在考虑选择器问题;行内样式 优先于 样式表或style标签的样式;选择器:id 优先于 class ;class 优先于 标签选择器;
  3. 源码顺序:前两条都一样的话,出现在后面的样式覆盖出前面样式。

注意:不要混淆CSS的属性property和HTML的属性attribute

//一个规则集ruleset由一个声明块和一个选择器组成

//一个声明块由包含在大括号内的一组声明组成

body {

//一个css声明由一个属性和一个值组成
            color: black;
            font-family: Helvetica;
          }

@规则(at-rules)是指用“@”符号开头的语法。比如@import规则或者@media查询。

1. 样式表的来源

样式表的来源有3种,其优先级由高到低分别是:作者样式表author stylesheets、用户样式表user stylesheets、用户代理样式表(浏览器默认样式)user-agent stylesheets。

User-Agent Style 用户代理样式,浏览器也只是用户代理中的一种。在不同浏览器上稍有差异,但是大体上是在做相同的事情:为标题(<h1>到<h6>)和段落(<p>)添加上下外边距,为列表(<ol>和<ul>)添加左侧内边距,为链接添加颜色,为元素设置各种默认字号。

1.1 用户代理样式

浏览器应用了用户代理样式后才会应用你的样式表,即作者样式表。你指定的声明会覆盖用户代理样式表里的样式(优先级更高,应用层叠的样式来源规则)。

用户代理(User Agent),是指浏览器,还包括搜索引擎。它的信息包括硬件平台、系统软件、应用软件和用户个人偏好。

注意:避免在样式表使用ID选择器,统一在样式表使用类选择器,ID选择器更应该用来作为JavaScript选中元素的钩子将后端传回的数据展现到页面。

1.2 !important声明

标记了!important的声明会被当作更高优先级的来源。通过!important声明后优先级会比内联样式还高,但同时这也体现出它的两面性,一旦使用了该声明,后面的修改就不易修改了。

2. 理解优先级

如果无法用来源解决冲突声明,浏览器会尝试检查它们的优先级。

99%的网站样式是来自同样的源。所以相同的源内的代码样式冲突应考虑优先级来解决。

浏览器将优先级分为两部分:HTML的行内样式和选择器的样式。

2.1 行内样式

行内元素属于“带作用域的”声明,它会覆盖任何来自样式表或者<style>标签的样式。行内样式没有选择器,因为直接作用于当前元素。

若要覆盖行内样式,需在样式表使用!important;但是若行内样式使用了!important就无法覆盖了,最好只在样式表内使用!important。

2.2 选择器优先级

有多层选择器的样式块中,指向越明确越具体的样式块优先级越高。

优先级的准确规则如下:

❑ 如果选择器的ID数量更多,则它会胜出(即它更明确)。

❑ 如果ID选择器数量一致,那么拥有最多类的选择器胜出。

❑ 如果以上两次比较都一致,那么拥有最多标签名的选择器胜出。

注意:伪类选择器(如:hover)和属性选择器(如[type="input"])与一个类选择器的优先级相同。通用选择器(*)和组合器(>、+、~)对优先级没有影响。

2.3 优先级标记

一个常用的表示优先级的方式是用数值形式来标记,通常用逗号隔开每个数。比如,“1,2,2”表示选择器由1个ID、2个类、2个标签组成。优先级最高的ID列为第一位,紧接着是类,最后是标签。

c92a710384524804a7943aa2905c1b6c.jpg

 有时还会用4个数的标记,其中将最重要的位置用0或1来表示,代表一个声明是否是用行内样式添加的。此时,行内样式的优先级为“1,0,0,0”。它会覆盖通过选择器添加的样式,比如优先级为“0,1,2,0”(1个ID和2个类)的选择器。

2.4 关于优先级的思考

  1. 谨慎使用!important:它可能会解决当前问题,但是在后续更改时又容易引入新问题。若修改时使用!important又会重新比较两处!important代码的来源,再比较两处代码的优先级规则,又会进入新一轮的比较,最好利用选择器优先级解决当前问题。
  2. 尽量降低样式代码块的总体优先级,在后续更改时才有更多空间。

3. 源码顺序

如果两个声明的来源和优先级相同,其中一个声明在样式表中出现较晚,或者位于页面较晚引入的样式表中,则该声明胜出。

面对一个样式问题时,将其分两个步骤来解决它。首先确定哪些声明可以实现效果。其次,思考可以用哪些选择器结构,然后选择最符合需求的那个。限制样式的范围,避免在不需要该样式的地方出现。

3.1 链接样式和源码顺序

链接样式中的四个状态优先级相同,当元素同时处于多个状态,最后个样式会覆盖前面样式。这个顺序的记忆口诀是“LoVe/HAte”(“爱/恨”),即link(链接)、visited(访问)、hover(悬停)、active(激活)。

3.2 层叠值

浏览器遵循三个步骤,即来源、优先级、源码顺序,来解析网页上每个元素的每个属性。如果一个声明在层叠中“胜出”,它就被称作一个层叠值。元素的每个属性最多只有一个层叠值。

层叠值——作为层叠结果,应用到一个元素上的特定属性的值。如果始终未指定一个属性,那这个属性就没有层叠值。

4. 两条经验法则

  • 不要使用id选择器;
  • 不要使用 !important;

当创建一个用于分发的JavaScript模块(比如NPM包)时,强烈建议尽量不要在JavaScript里使用行内样式。如果这样做了,就是在强迫使用该包的开发人员要么全盘接受包里的样式,要么给每个想修改的属性加上!important。

正确的做法是在包里包含一个样式表。如果组件需要频繁修改样式,通常最好用JavaScript给元素添加或者移除类。这样用户就可以在使用这份样式表的同时,在不引入优先级竞赛的前提下,按照自己的喜好选择编辑其中的样式。(《JavaScript高级程序设计》有提到)


  • 如何处理优先级
  • 在哪里可以放心使用 !important

二、继承

如果一个元素的某个属性没有层叠值,则可能会继承某个祖先元素的值。继承属性从DOM树的父节点传递到后代节点。

但不是所有的属性都能被继承。默认情况下,只有特定的一些属性能被继承,通常是我们希望被继承的那些,比如与文本相关的部分属性、与列表相关的部分属性、与表格相关的部分属性。

继承属性会顺序传递给后代元素,直到它被层叠值覆盖。

如何使用开发工具追踪层叠值和覆盖值?

使用浏览器开发者工具能够看到哪些元素应用了哪些样式规则,以及为什么应用这些规则。下图是某个元素的规则集示例

48ea5985f3554a0da06d24c8e89b11ce.jpg

  •  该元素上的选择器按优先级从高到低排列,靠近顶部的样式覆盖下面的样式,被覆盖的属性用删除线表示;
  • 在选择器下方是继承inherited属性,可以看到该元素继承了哪些属性以及继承属性的来源;
  • 右侧可以显示这些规则集的来源和位置,比如是用户代理样式、用户样式还是作者样式,以及在对应样式表的行号。

三、特殊值

有两个特殊值可以赋给任意属性,用于控制层叠:inherit(继承其父元素的值)和initial(恢复元素初始值)。

1. inherit关键字

如果想用继承代替一个层叠值,这时候可以用inherit关键字。

还可以使用inherit关键字强制继承一个通常不会被继承的属性,比如边框和内边距。

2. initial关键字

每一个CSS属性都有初始(默认)值。如果将initial值赋给某个属性,那么就会有效地将其重置为默认值,这种操作相当于硬复位了该值。

initial 恢复的是该属性的默认值,而非某个元素的原始值,两者不一定在所有场合下保持一致。

说明:display: initial等价于display: inline。不管应用于哪种类型的元素,它都不会等于display: block。这是因为initial重置为属性的初始值,而不是元素的初始值。inline才是display属性的初始值。

属性默认值与元素的样式属性默认值有区别,相同属性在不同元素上初始值可能不同。

四、简写属性

简写属性是用于同时给多个属性赋值的属性。

比如font、background、border、border-width等。

简写属性是用于某一类属性的样式设置,省略的属性值会被设置成默认值,有可能覆盖其他地方的样式。

1. 简写属性会默默覆盖其他样式

大多数简写属性可以省略一些值,只指定我们关注的值。但是要知道,这样做仍然会设置省略的值,即它们会被隐式地设置为初始值。这会默默覆盖在其他地方定义的样式。

在所有的简写属性里,font的问题最严重,因为它设置的属性值太多了。因此,要避免在<body>元素的通用样式以外使用font。

简写属性主要是简单明了的用处。虽然在一定程度上可以缩减css文件大小,有利于提升网络交互速度,但是缩减的体积几乎可以忽略不计。

2. 理解简写值的顺序

简写属性会尽量包容指定的属性值的顺序。可以设置border: 1px solid black或者border: black 1px solid,两者都会生效。

使用简写属性时,如果属性值的类型各不相同,那么顺序无关紧要,反之,很重要。

2.1 上右下左

这些属性的值是按顺时针方向,从上边开始的。

这种模式下的属性值还可以缩写。如果声明结束时四个属性值还剩一个没指定,没有指定的一边会取其对边的值。指定三个值时,左边和右边都会使用第二个值。指定两个值时,上边和下边会使用第一个值。如果只指定一个值,那么四个方向都会使用这个值。

大多数情况只需要指定两个值。尤其对于较小的元素,左右的内边距最好大于上下内边距。这种样式很适合网页的按钮或者导航链接。

2.2 水平垂直

还有一些属性只支持最多指定两个值,这些属性包括background-position、box-shadow、text-shadow(虽然严格来讲它们并不是简写属性)。这两个值代表了一个笛卡儿网格。笛卡儿网格的测量值一般是按照x, y(水平,垂直)的顺序来的。

如果属性需要指定从一个点出发的两个方向的值,就想想“笛卡儿网格”。如果属性需要指定一个元素四个方向的值,就想想“时钟”。

五、总结

  • 控制选择器的优先级
  • 不要混淆层叠和继承
  • 继承的一般是与文本、列表、表格相关的部分属性
  • 不要混淆initial和auto,auto只是某些属性的默认值,比如width
  • 简写属性注意顺时针方向和笛卡尔坐标系方向

 

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值