CSS从小白到大神(三)层叠与继承(优先级・权重计算)

创建日期: 2020年4月日
参考链接

准备工作:
如果你是新打开这篇文章,那在开始之前,请参看CSS实验道具,打开CSS实验的在线工具。

题记:
本章节,原文在整个CSS体系的第一篇,但是考虑到对CSS不甚了解的童鞋们,可能会很糊涂。因此,笔者将其移至选择器之后来讲解。

前言:
  本文旨在让你理解CSS的一些最基本的概念——层叠、优先级和继承——这些概念决定着如何将CSS应用到HTML中,以及如何解决冲突。
  尽管与课程的其他部分相比,完成这节课可能看起来没有那么直接的相关性,而且更学术性一些,但是理解这些东西将为您以后节省很多痛苦! 我希望您仔细阅读本节,并在继续下一步学习之前,确保您是否理解了这些概念。

1.冲突规则

css代表层叠样式表,理解第一个词cascading很重要— cascade 的表现方式是理解CSS的关键。

在某些时候,在做一个项目过程中你会发现一些应该产生效果的样式没有生效。通常的原因是你创建了两个规则同时对相同的元素生效。cascade, 和它密切相关的概念是 specificity,决定在发生冲突的时候应该使用哪条规则。设计元素样式的规则可能不是期望的规则,因此需要了解这些机制是如何工作的。

这里也有继承的概念,也就是在默认情况下,一些css属性继承当前元素的父元素上设置的值,有些则不继承。这也可能导致一些和期望不同的结果。

我们来快速的看下正在处理的关键问题,然后依次了解它们是如何相互影响的,以及如何和css交互的。看起来像是一头很难理解的概念,但是在练习写css时,它的工作方式会让你感觉更加清晰。

1.1 层叠

Stylesheets cascade(样式表层叠) —– 简单的说,css规则的顺序很重要;当应用两条同级别的规则到一个元素的时候,写在后面的就是实际使用的规则。

下面的例子中,我们有两个关于 h1 的规则。h1 最后显示蓝色 — 这些规则有相同的优先级,所以顺序在最后的生效。

h1 { 
   color: red; 
}
h1 { 
   color: blue; 
}
<h1>This is my heading.</h1>

实验结果:

1.2 优先级

浏览器是根据优先级来决定当多个规则有不同选择器对应相同的元素的时候需要使用哪个规则。它基本上是一个衡量选择器具体选择哪些区域的尺度:

  • 一个元素选择器不是很具体 —– 会选择页面上该类型的所有元素,所以它的优先级就会低一些。
  • 一个类选择器稍微具体点 —– 它会选择该页面中有特定 class 属性值的元素,所以它的优先级就要高一点。

举例时间! 下面我们再来介绍两个适用于 h1 的规则。下面的 h1 最后会显示什么颜色呢?只看代码不运行,猜一下结果。

.main-heading { 
    color: red; 
}     
h1 { 
    color: blue; 
}
<h1 class="main-heading">This is my heading.</h1>

实验结果:
类选择器有更高的优先级,因此就会被应用,即使元素选择器顺序在它后面。所以,最终会显示红色。你猜对了吗?

优先级有评分,也就是CSS的权重,稍后,我们会具体讲解。

1.3 继承

继承也需要在上下文中去理解 —— 一些设置在父元素上的css属性是可以被子元素继承的,有些则不能。

举一个例子,如果你设置一个元素的 color 和 font-family ,每个在里面的元素也都会有相同的属性,除非你直接在元素上设置属性。

body {
    color: blue;
}
span {
    color: black;
}
<p>As the body has been set to have a color of blue this is inherited through the descendants.</p>
<p>We can change the color by targetting the element with a selector, such as this <span>span</span>.</p>

实验结果:

这里,初学者可能有点懵,body这个元素选择器从何而来?在我们的测试模板中,最外层就是用<body>来包裹的。

一些属性是不能继承的。举个例子如果你在一个元素上设置 width 50% ,所有的后代不会是父元素的宽度的50% 。如果这个也可以继承的话,CSS就会很难使用了!


2.层叠,优先级,继承––––三者的协同工作

这三个概念一起来控制css规则应用于哪个元素;在下面的内容中,我们将看到它们是如何协同工作的。有时候会感觉有些复杂,但是当你对css有更多经验的时候,你就可以记住它们,即便忘记了细节,可以在网上查到,有经验的开发人员也不会记得所有细节。

2.1 理解继承

我们从继承开始。下面的例子中我们有一个ul,里面有两个无序列表。我们已经给 <ul> 设置了 borderpaddingfont color

color应用在直接子元素,也影响其他后代 —–– 直接子元素<li>,和第一个嵌套列表中的子项。然后添加了一个 special 类到第二个嵌套列表其中使用了不同的颜色。然后通过它的子元素继承。

.main {
    color: green;
    border: 2px solid red;
    padding: 1em;
}
.special {
    color: black;
    font-weight: bold;
}
<ul class="main">
    <li>Item One</li>
    <li>Item Two
        <ul>
            <li>2.1</li>
            <li>2.2</li>
        </ul>
    </li>
    <li>Item Three
        <ul class="special">
            <li>3.1
                <ul>
                    <li>3.1.1</li>
                    <li>3.1.2</li>
                </ul>
            </li>
            <li>3.2</li>
        </ul>
    </li>
</ul>

实验结果:

像 widths (上面提到的), margins, padding, 和 borders 不会被继承。如果borders可以被继承,每个列表和列表项都会获得一个边框 — 可能就不是我们想要的结果!

哪些属性属于默认继承很大程度上是由常识决定的。

2.1.1控制继承

CSS 为控制继承提供了四个特殊的通用属性值。每个css属性都接收这些值。

  • inherit 设置该属性会使子元素属性和父元素相同。实际上,就是 "开启继承".
  • initial 设置属性值和浏览器默认样式相同。如果浏览器默认样式中未设置且该属性是自然继承的,那么会设置为 inherit 。
  • unset 将属性重置为自然值,也就是如果属性是自然继承那么就是 inherit,否则和 initial一样。
  • revert 这是一个新属性值,只有很少的浏览器支持。【待调查】

下面的例子你可以通过修改css来查看它们的功能。我们带着问题,来运行下面的例子:

  1. 第二个列表项有类 my-class-1 。它设置了内部元素来继承属性。如果你删除这个类,它会如何改变链接的颜色?
  2. 你知道为什么第三个和第四个链接会是这样的颜色?
  3. 哪个链接的颜色会改变?如果你对 <a> 添加一个新样式 。 例如 a { color: red; }?
/* CSS */
body {
    color: green;
}          
.my-class-2 a {
    color: inherit;
}         
.my-class-3 a {
    color: initial;
}         
.my-class-4 a {
    color: unset;
}
<!--  HTML -->
<ul>
    <li>Default <a href="#">link</a> color</li>
    <li class="my-class-2">Inherit the <a href="#">link</a> color</li>
    <li class="my-class-3">Reset the <a href="#">link</a> color</li>
    <li class="my-class-4">Unset the <a href="#">link</a> color</li>
</ul>

实验结果:
这里我们不贴图了,你可以自己在CSS在线工具上运行。我们解答下上述3个问题:

  1. 链接颜色会和第一个列表相同,默认的蓝色;
  2. 第三个链接,设置了initial属性,会采用浏览器的默认样式,笔者显示的是黑色;第四个链接,设置了unset,将属性重置为自然值,也就是如果属性是自然继承那么就是 inherit,否则和 initial一样。这里,属性是自然继承,链接为绿色。
  3. 拆入a标签的选择器后,只有第一个列表的链接变成红色。其余三个都是类选择器,优先级高于元素选择器,因此都是各自的类选择器优先起作用。

2.1.2 重设所有属性值

CSS 的 shorthand 属性 all 可以用于同时将这些继承值中的一个应用于(几乎)所有属性。它的值可以是其中任意一个(inherit, initial, unset, or revert)。这是一种撤销对样式所做更改的简便方法,以便回到之前已知的起点。

下面的例子中有两个blockquote 。第一个用元素本身的样式 ,第二个设置 all 为 unset:

blockquote {
    background-color: red;
    border: 2px solid green;
}      
.fix-this {
    all: unset;
}
<blockquote>
    <p>This blockquote is styled</p>
</blockquote>

<blockquote class="fix-this">
   <p>This blockquote is not styled</p>
</blockquote>

实验结果:

.fix-this {all: unset;} .fix-this {background-color: unset;}

第二幅图,我们将all改成了background-color,因此仅有元素选择器的border的属性被选用了。而background-color因为设置为uset,实际上是继承了父标签body的背景颜色了。我们可以插入下面这句CSS代码:

body {
    background-color: yellow;
}

然后,你会发现,第二个<blockquote>背景颜色变成了黄色。这样,更明显的让你理解,uset的设置属性,实际默认继承了父标签<body>的背景颜色。

2.2 理解层叠

  我们现在明白了为什么嵌套在html结构中的段落和应用于正文中的css颜色相同,从入门课程中,我们了解了如何将文档中的任何修改应用于某个对象的css,无论是把css指定某个元素还是创建一个类。现在,我们将要了解层叠如何定义在不止一个元素的时候怎么应用css规则。

有三个因素需要考虑,根据重要性排序如下,前面的更重要:

  • 重要程度
  • 优先级
  • 资源顺序

我们从下往上,看看浏览器是如何决定该应用哪个css规则的。

2.2.1 资源顺序

我们已经看到了顺序对于层叠的重要性。如果你有超过一条规则,而且都是相同的权重,那么最后面的规则会应用。可以理解为后面的规则覆盖前面的规则,直到最后一个开始设置样式。

2.2.2 优先级

在你了解了顺序的重要性后,会发现在一些情况下,有些规则在最后出现,但是却应用了前面的规则。这是因为前面的有更高的优先级 —– 它范围更小,因此浏览器就把它选择为元素的样式。

就像前面看到的,类选择器的权重大于元素选择器,因此类上定义的属性将覆盖应用于元素上的属性。

这里需要注意虽然我们考虑的是选择器,以及应用在选中对象上的规则,但不会覆盖所有规则,只有相同的属性。

这样可以避免重复的 CSS。一种常见的做法是给基本元素定义通用样式,然后给不同的元素创建对应的类。举个例子,在下面的样式中我给2级标题定义了通用样式,然后创建了一些类只修改部分属性的值。最初定义的值应用于所有标题,然后更具体的值通过对应类来实现。

h2 {
    font-size: 2em;
    color: #000;
    font-family: Georgia, 'Times New Roman', Times, serif;
}
.small {
    font-size: 1em;
}    
.bright {
    color: red;
}
<!-- HTML -->
<h2>Heading with no class</h2>
<h2 class="small">Heading with class of small</h2>
<h2 class="bright">Heading with class of bright</h2>

实验结果:

(1) 优先级的计算

现在让我们来看看浏览器如何计算优先级。
我们已经知道一个元素选择器比类选择器的优先级更低会被其覆盖。本质上,不同类型的选择器有不同的分数值,把这些分数相加就得到特定选择器的权重,然后就可以进行匹配。

一个选择器的优先级可以说是由四个部分相加 (分量),可以认为是个十百千 — 四位数的四个位数:

  1. 千位: 如果声明在 style的属性(内联样式)则该位得一分。这样的声明没有选择器,所以它得分总是1000。
  2. 百位: 选择器中包含ID选择器则该位得一分。
  3. 十位: 选择器中包含类选择器、属性选择器或者伪类则该位得一分。
  4. 个位:选择器中包含元素、伪元素选择器则该位得一分。

注: 通用选择器 (*),组合符 (+, >, ~, ' '),和否定伪类 (:not) 不会影响优先级。

‼️警告: 在进行计算时不允许进行进位,例如,20 个类选择器仅仅意味着 20 个十位,而不能视为 两个百位,也就是说,无论多少个类选择器的权重叠加,都不会超过一个 ID 选择器。

下面有几个单独的例子,试着思考下:

选择器 千位 百位 十位 个位 优先级
h1 0 0 0 1 0001
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="en-US"] > .inline-warning 0 0 2 2 0022
#identifier 0 1 0 0 0100
内联样式 1 0 0 0 1000

(不明白的可以留言,笔者在线的时候会给你解答)

示例:在运行前,建议你自己思考下,会是一个什么样的布局呢?

/* CSS */
/* specificity: 0101 */
#outer a {
    background-color: red;
}
        
/* specificity: 0201 */
#outer #inner a {
    background-color: blue;
}

/* specificity: 0104 */
#outer div ul li a {
    color: yellow;
}

/* specificity: 0113 */
#outer div ul .nav a {
    color: white;
}

/* specificity: 0024 */
div div li:nth-child(2) a:hover {
    border: 10px solid black;
}

/* specificity: 0023 */
div li:nth-child(2) a:hover {
    border: 10px dashed black;
}

/* specificity: 0033 */
div div .nav:nth-child(2) a:hover {
    border: 10px double black;
}

a {
    display: inline-block;
    line-height: 40px;
    font-size: 20px;
    text-decoration: none;
    text-align: center;
    width: 200px;
    margin-bottom: 10px;
}

ul {
    padding: 0;
}

li {
    list-style-type: none;
}  
<!-- HTML -->
<div id="outer" class="container">
    <div id="inner" class="container">
        <ul>
            <li class="nav"><a href="#">One</a></li>
            <li class="nav"><a href="#">Two</a></li>
        </ul>
    </div>
</div>

运行结果:


鼠标放在第二个框上,会出现双直线黑色border。(不懂留言,笔者在线时会给解答)

(2) !important

有一个特殊的 CSS 可以用来覆盖所有上面所有优先级计算,不过需要很小心的使用 — !important。用于修改特定属性的值, 能够覆盖普通规则的层叠。

看看这个例子,有两个段落,其中一个有ID。

/* CSS */
#winning {
    background-color: red;
    border: 1px solid black;
}   
.better {
    background-color: gray;
    border: none !important;
}  
p {
    background-color: blue;
    color: white;
    padding: 5px;
}            
<!-- HTML -->
<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>

运行结果:

image.png

注: 覆盖 !important 唯一的办法就是另一个 !important 具有 相同优先级 而且顺序靠后,或者更高优先级。

了解 !important 是为了在阅读别人代码的时候知道有什么作用。 但是,强烈建议除了非常情况不要使用它。 !important 改变了层叠的常规工作方式,它会使调试 CSS 问题非常困难,特别是在大型样式表中。

在一种情况下,你可能不得不使用它:当你不能编辑核心的CSS模块,不能用任何其他方式覆盖,而你又真的想要覆盖一个样式时。但说真的,如果可以避免的话就不要用它。

3. CSS位置的影响

最后,也很有用,CSS声明的重要性取决于样式表中指定的——它让用户可以设置自定义样式表来覆盖开发人员定义的样式。例如用户可能视力受损,并想在所有网页上设置两倍的正常字体大小,以便更容易进行阅读。

4. 简而言之

相互冲突的声明将按以下顺序适用,后一种声明将覆盖前一种声明:

  1. 用户代理样式表中的声明(例如,浏览器的默认样式,在没有设置其他样式时使用)。
  2. 用户样式表中的常规声明(由用户设置的自定义样式)。
  3. 作者样式表中的常规声明(这些是我们web开发人员设置的样式)。
  4. 作者样式表中的!important声明
  5. 用户样式表中的!important 声明

对于web开发人员的样式表来说,覆盖用户样式表是有意义的,因此设计可以按预期进行,但是有时用户充足的理由覆盖web开发人员样式,正如上面提到的—––这可以通过在他们的规则中使用!important来实现。

如果,你还是糊涂,也没有关系,即使是专业的web开发人员有时也会觉得棘手。在接下来的学习中,反复看看优先级部分,加深理解和记忆。你也可以自己在实验工具上,更改CSS规则,多练习几次。


CSS系列 目录导引

上一篇:CSS从小白到大神(二)CSS选择器 4关系选择器
下一篇待更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容