文章目录
- 1.1.2 行内样式
- 1.1.3 选择器的优先级
- 1.1.3.1 优先级的写法
- 1.1.3.2 关于优先级的思考
1.1.2 行内样式
如果无法通过样式表来源规则解决样式冲突,浏览器则会考察它们是否通过 行内样式 作用于该元素。当使用 HTML 的 style
属性声明样式时,该样式仅对当前元素生效,从而覆盖来自样式表或 <style>
标签的声明。行内样式没有选择器,因为它们直接作用于当前所在元素。
根据示例页头的最终效果,需要将导航菜单中的特色链接(即末尾的 Special 项)设为橙黄色背景,如图 1.6 所示。实现这一效果有很多种方法。先从代码清单 1.4 提供的内联样式开始。
图 1.6 内联样式覆盖了选择器的样式
想查看实测效果,按以下代码修改样式即可(稍后将撤销这部分更改)。
代码清单 1.4 行内样式
<li><!-- 通过 style 属性设置行内样式 --><a href="/specials" class="featured" style="background-color: orange;"> Specials</a>
</li>
想要在样式表覆盖掉行内样式,需要给样式加一个 !important
标记,将其提升为优先级更高的样式表来源;但要是行内样式也有 !important
标记,那就彻底没戏了。因此最好只在样式表内使用 !important
。下面撤销代码清单 1.4 中的样式,来看看更好的实现方式。
1.1.3 选择器的优先级
优先级(Specificity) 是 CSS 学习过程中一个容易漏过、但又至关重要的语言特性。不理解样式表来源的概念倒也不妨碍您开发 CSS,因为绝大部分样式都是您自己写的,来源上都属于作者样式;但要是不理解 CSS 的优先级规则,几乎可以肯定是要踩坑的。
如果前面两个规则依旧无法解决样式冲突,浏览器则会进一步考察它们的选择器优先级。譬如,含有两个类名(class names
)的选择器,优先级就比仅有一个的高;一个样式声明元素背景为橙黄色,但优先级更高的另一个声明却将其设为茶青色(teal),最终浏览器会用茶青色作背景。
下面举例说明,看看用一个简单的类选择器让特色链接的背景色设为橙黄色是什么效果。更新样式代码如下:
代码清单 1.5 不同优先级的选择器
#main-nav a { /* 更高优先级的选择器 */color: white;background-color: #13a4a4; /* 茶青色背景 */padding: 5px;border-radius: 2px;text-decoration: none;
}
.featured { /* 由于优先级较低,橙黄色背景无法覆盖茶青色背景 */background-color: orange;
}
运行后,橙黄色没有生效!所有导航链接仍然是茶青色。怎么回事?因为此时第一个选择器比第二个更具体(more specific)、优先级更高。它由一个 ID 和一个标签名组成;而第二个仅有一个类名。但仅凭选择器的长度还不足以说明问题。
选择器的类型不同,其优先级也不同。例如,ID 选择器的优先级就比类选择器更高。实际上,单个 ID 的优先级比包含任意多个类的选择器都高。类似地,类选择器的优先级也比含有任意多个标签的标签选择器(即 tag selector
,也称为类型选择器,亦即 type selector
)的优先级更高。
选择器优先级的具体规则如下:
- 如若一方选择器 ID 数更多,则更多者胜出(即更具体、更明确);
- 如若 ID 数量一致,则 类数更多 者胜出;
- 如若上述比较均一致,则 标签名更多 者胜出。
考察以下代码中的选择器(注意不要添加到您的示例页)。它们是按照优先级从低到高的顺序排列的:
代码清单 1.6 具有逐渐增加特异性的选择器
html body header h1 { /* 4 个标签 */color: blue;
}
body header.page-header h1 { /* 3 个标签和 1 个类 */color: orange;
}
.page-header .title { /* 2 个类 */color: green;
}
#page-title { /* 1 个 ID */color: red;
}
描述最具体的选择器是 #page-title
,它有一个 ID,因此标题颜色最终为红色。第二具体的是 .page-header .title
,它有两个类名。如果没有后面的 ID 选择器,则会生效该选择器样式(绿色)。第三个选择器 .page-header .title
比第二个(body header.page-header h1
)优先级更高,尽管长度不及第二个。因为两个类比一个类更具体。最后,第一个选择器 html body header h1
只有四个元素类型(即标签名),没有 ID 或类,因此优先级是最低的。
注意
伪类选择器(
Pseudo-class selectors
,如:hover
)以及属性选择器(attribute selectors
,如[type="input"]
)的优先级与一个类选择器相同。通用选择器(*
)和组合器(>
、+
、~
)对优先级没有贡献。更多选择器类型信息,请参阅附录 A。
如果添加一个 CSS 样式声明但没有生效,往往是由于被优先级更高的样式覆盖了。许多时候开发人员用到了 ID 选择器,却没考虑过这样会创建更高的优先级,后面再想用其他样式覆盖就很难了。覆盖一个使用了 ID 选择器的样式,就只能用另一个 ID 尝试夺回优先级。
这个概念很简单,但如果不理解优先级,就可能始终都想不明白为什么一个样式能生效,而另一个却不能。
1.1.3.1 优先级的写法
优先级的一个常见写法,是用一组由逗号分隔的数字来表示。例如,1,2,2
表示该选择器包含 1 个 ID、2 个类、2 个标签。优先级最高的 ID 排在首位,其次是类,最后是标签。
选择器 #page-header #page-title
有 2 个 ID,没有类、也没有标签,其优先级可以写作 2,0,0
;再比如选择器 ul li
,有 2 个标签,但既没有 ID 也没有类,优先级就是 0,0,2
。表 1.1 给出了代码清单 1.6 中各种选择器的优先级的写法。
表 1.1 各种选择器及其对应的优先级
选择器 | ID | 类(Classes) | 标签(Tags) | 写法(Notation) |
---|---|---|---|---|
html body header h1 | 0 | 0 | 4 | 0,0,4 |
body header.page-header h1 | 0 | 1 | 3 | 0,1,3 |
.page-header .title | 0 | 2 | 0 | 0,2,0 |
#page-title | 1 | 0 | 0 | 1,0,0 |
至此,判定优先级大小的问题就变成了纯数字比较。1,0,0
的优先级要高于 0,2,2
,甚至高于 0,10,0
(尽管我强烈不推荐写一个含有 10 个类名的选择器),因为第一个数字(代表 ID 数)享有最高的优先级。
业内偶尔也用四位数字来表示优先级。其最高位为 0
或 1
,表示该声明是否通过 行内样式 进行指定。因为在 CSS 规范的早期版本中,行内样式最初被定义为优先级的一个子集。这样一来,行内样式的优先级写法就成了 1,0,0,0
。这将覆盖通过选择器设置的样式,比如优先级像 0,1,2,0
(1 个 ID 和 2 个类)的选择器。
1.1.3.2 关于优先级的思考
之前通过类选择器 .featured
设置橙黄色背景没有奏效,因为有个包含 ID 的选择器 #main-nav a
覆盖了原本的类选择器(优先级分别为 1,0,1
和 0,1,0
)。有很多种方法可以解决这个问题,接下来介绍几种可行方案。
最省事的一个方法是在目标声明中添加 !important
。按以下代码更新样式:
代码清单 1.7 试行方案一
#main-nav a {color: white;background-color: #13a4a4;padding: 5px;border-radius: 2px;text-decoration: none;
}
.featured {/* 标为重要声明,以享有更高优先级来源 */background-color: orange !important;
}
这次生效了,因为 !important
标记将声明提升到了优先级更高的样式表来源。这个方法的确省事,但也很低级。它可能暂时解决了眼前的问题,但也给您的后续开发挖了坑。一旦给多处声明标注了 !important
,后面再想覆盖掉这些样式又该如何是好?这时浏览器将再次启用样式表来源规则,进而再启用常规的优先级比较规则,最终在绕了一大圈后又让一切回到了原点。
就没有更好的解决方案了吗?与其考虑绕开选择器优先级,不如因势利导为我所用:何不主动提升选择器的优先级呢?将以下代码更新到您的示例文件中:
代码清单 1.8 试行方案二
#main-nav a { /* 优先级仍然为 1,0,1 */color: white;background-color: #13a4a4;padding: 5px;border-radius: 2px;text-decoration: none;
}
#main-nav .featured { /* 优先级提升为 1,1,0 */background-color: orange; /* 此时 !important 标记已经没用了 */
}
这样改也能生效。此时选择器包含 1 个 ID 和 1 个类,优先级为 1,1,0
,比 #main-nav a
(优先级 1,0,1
)更高,所以最终背景变成了橙黄色。
该方法还有改进空间。与其抬升第二个选择器的优先级,不妨试试降低第一个选择器的优先级。注意到该元素包含一个类:<ul id="main-nav" class="nav">
,因此您可以通过类名而非 ID 来选中该元素。按如下代码将选择器 #main-nav
变更为 .nav
。
代码清单 1.9 试行方案三
.nav { /* 将样式表中的 “#main-nav” 全部改为 “.nav” */margin-top: 10px;list-style: none;padding-left: 0;
}.nav li { /* 将样式表中的 “#main-nav” 全部改为 “.nav” */display: inline-block;
}.nav a { /* 第一个选择器的优先级降为 (0,1,1) */color: white;background-color: #13a4a4;padding: 5px;border-radius: 2px;text-decoration: none;
}
.nav .featured { /* 第二个选择器的优先级升至 (0,2,0) */background-color: orange;
}
至此,通过降低选择器的优先级,橙黄色背景的优先级已经足以覆盖掉之前的茶青色背景。
通过这些例子不难发现,优先级的对比容易沦为“军备竞赛”。在大型项目中这一点尤为突出。因此公认的最佳实践是:尽可能让优先级保持低位运行,以便将来需要覆盖样式时,您能有更多回旋的余地。