解决层叠冲突的最后一环叫做 源码顺序,有时又称为 出现顺序(order of appearance)。如果其他判定规则均一致,则样式表中后出现的、或者在页面较晚引入的样式表声明,将最终胜出。
也就是说,可以通过控制源码出现的顺序来给示例中的特色链接添加样式。如果两个存在冲突的选择器优先级也相同 ,则出现较晚的一方将胜出。再来看看下面给出的第四种样式实现方案。这里用到了一个复合选择器 a.featured
,中间没有空格。该选择器指向同时满足两个条件的元素:带 featured
类的 <a>
。
代码清单 1.10 试行方案四
.nav a {
color: white;
background-color: #13a4a4;
padding: 5px;
border-radius: 2px;
text-decoration: none;
}
a.featured { /* 优先级变为 (0,1,1) */
background-color: orange;
}
该方案中的两个选择器优先级相等,源码顺序决定了哪个声明实际作用于特色链接,最终获得一个橙黄色背景。
问题看似解决了,但也引入了一个潜在的新问题:如果想在页面其他位置(导航菜单之外)沿用 featured
类,情况又如何呢?如图 1.7 所示,在页面其他位置添加一段内容 <a class="featured">our specials</a>
,会得到一种奇怪的混合样式:背景还是橙黄色,但导航链接的文字颜色、内边距及圆角半径信息都丢失了。
图 1.7 在导航之外使用 featured 类产生的怪异样式
代码清单 1.11 给出了上述效果的 HTML 代码。目标元素仅被第二个选择器命中,而不受第一个影响,因此最终效果未能如愿。是否希望这个橙黄色按钮的样式在导航菜单以外的地方生效,得由您自己来定。如果确实需要,就必须把相关的样式一并带上。
代码清单 1.11 导航菜单外部的特色链接样式
<header class="page-header">
<h1 id="page-title" class="title">Wombat Coffee Roasters</h1>
<nav>
<ul id="main-nav" class="nav">
<li><a href="/">Home</a></li>
<li><a href="/coffees">Coffees</a></li>
<li><a href="/brewers">Brewers</a></li>
<li><a href="/specials" class="featured">Specials</a></li>
</ul>
</nav>
</header>
<main>
<p>
Be sure to check out
<!-- nav 外的特色链接只保留了部分样式 -->
<a href="/specials" class="featured">our specials</a>.
</p>
</main>
除非网站有其他需求,否则我更倾向于第三种方法(代码清单 1.9)。理想情况下,您可以对其他位置可能出现的样式需求做出合理的预判,比如确定在别处也会用到这个特色链接,这种情况下,方案四(代码清单 1.10)也许更合适,当然也要注意补全 featured
类缺失的样式。
正如之前所说,在 CSS 中最好的答案通常是“这要看情况”。很多样式的效果都是一题多解,找出不同的实现方法并知晓每种方法的利弊,是非常有价值的。面对同一个样式问题,通常我会分两步来处理:首先找出能达到效果的几个方案,然后再思考其对应的选择器结构,并选出最符合需求的那个方案。
1 链接样式和源码顺序
在您刚开始学习 CSS 时可能就已经了解,要给链接设计样式,书写选择器时就必须遵循特定的顺序。这是因为源码顺序影响了层叠。以下代码给出了链接样式设计的正确打开方式。将它们添加到样式表的开头、nav
样式的前面:
代码清单 1.12 超链接样式
a:link {
background-color: blue;
color: white;
text-decoration: none;
padding: 2px;
}
a:visited {
background-color: purple;
}
a:hover {
background-color: transparent;
color: blue;
text-decoration: underline;
}
a:active {
color: red;
}
层叠才是链接顺序之所以重要的核心本质:在优先级相同的情况下,靠后的样式会覆盖靠前的样式。如果一个元素同时拥有多个状态,则最后那个状态的样式会覆盖其他的状态。比如当用户悬停在已访问的链接上,则悬停样式生效;当用户在悬停状态下激活(activate,即点击它)该链接,激活样式会最终生效。
这个顺序有个不错的记忆口诀:LoVe/HAte (爱/恨法则)—— 即 link(链接)、visited(已访问)、hover(悬停)、active(激活)。注意,如果当中选择器的优先级改得与其他选择器不同,这个规则就会遭到破坏,并得到意想不到的结果。
小贴士
您还可以用
:any-link
伪类来匹配:link
或:visited
状态下的链接。
2 层叠值
浏览器按照上面介绍的这些规则来解析页面上每个元素的每个 property
属性。在层叠规则中胜出的样式声明,称为一个 层叠值。元素的每个属性最多只有一个层叠值。例如页面上一个特定的段落,它可以拥有一上一下两个外边距,但不能同时具有两个不同的上外边距,下外边距也一样。如果 CSS 为同一个属性指定了不同的值,层叠规则最终会选出一个值来渲染元素,这个值就是层叠值。
层叠值
即作为层叠结果最终应用到某元素特定属性上的具体取值。
如果一个元素从未指定某个属性值,则该属性没有层叠值。还是以段落为例,可能就没有特定的边框或内边距。
3 越用越顺手的层叠规则
在处理层叠时曾经有两条通用的经验法则:一是不要在选择器中使用 ID;二是不要使用 !important
注解。一旦今后要覆盖这些样式,这两条经验法则似乎都只能帮倒忙。
它们看似没多大用处,但事无绝对。在当前的许多情况下实践这两条经验法则反而反响良好,尤其是在需要考虑图层和作用域的场景下;另一方面也要牢记,切莫为了赢得优先级的“军备之争”而习惯性地套用这两种方法。本书第三部分还将探讨一些控制层叠规则的现代化工具,并展示几个可以打破这些规则的示例。
关于重要性的一则重要提醒
当创建一个像 NPM 包这样的用于分发的 JavaScript 模块时,强烈建议尽量避免在 JavaScript 里使用行内样式。要是这样做了,那就相当于强迫调用该模块的开发人员站队:要么照单全收里面的所有样式,要么给每个需要修改的
property
属性统一添加!important
标记。正确的做法是在包内放一张样式表。如果组件需要频繁修改样式,通常最好是用 JavaScript 给元素添加或移除某个样式类。这样用户就可以放心使用您提供的样式,并根据需要做一些调整,而不用担心优先级带来的竞争问题。