在您日常编写 CSS 的日子里,您可能根本不会想到 CSS 优先级。 它并不总是出现。 但它确实很重要! 每当多个 CSS 选择器匹配具有完全相同 特定性 的元素时,它就会出现。
假设特定性完全相同,顺序 *确实* 重要。
*最后* 声明的样式获胜。
在单个样式表中
假设我们有一些像这样的 HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="module module-foo module-bar">
Module
</div>
</body>
</html>
HTML 中属性的顺序*并不* 重要。 样式表中的顺序很重要。 我们来关注一下 background
/*
All of these selectors match
and they all have the same specificity
*/
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
.module-foo {
background: orange;
}
/* LAST declared, so these property/values win */
.module-bar {
background: lightblue; /* I win! */
/* I still have all the _other_ styles as well */
}
一个有意复杂的示例
顺序*不* 限于单个样式表。 文档中样式表顺序甚至更加重要。
查看这个文档,它包含三个不同的样式……嗯……让我们称之为块。 块可以是 <link rel="stylesheet">
、<style>
块或 @import
的样式表。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- #1 -->
<link rel="stylesheet" href="1.css">
<!-- #2 -->
<style>
.module-baz {
background-color: pink;
}
</style>
</head>
<body>
<div class="module module-foo module-bar module-baz">
Module
</div>
<!-- #3 -->
<style>
@import "2.css";
/*
Contains
.module-bar { background: #f06d06; }
*/
</style>
</body>
</html>
我将这些块标记为 #1、#2 和 #3。 它们都包含具有完全相同特定性的 CSS 选择器。 #3 是最后一个声明的,所以它赢得了优先级之战。
异步加载的 CSS 仍然尊重文档顺序
也许您正在使用像 loadCSS 这样的出色 CSS 加载器来加载 CSS。 如果我们用它来加载第四个 CSS 文件,并且它与上面“复杂”示例中的设置完全相同,会发生什么?
loadCSS 默认情况下将样式表注入到 <head>
的底部,因此它将成为 #3,而 body 底部的 <style>
块将成为 #4,因此获胜。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="1.css">
<script src="loadCSS.js"></script>
<script>
loadCSS("2.css");
</script>
<!-- 2.css will be injected right here -->
</head>
<body>
<div class="module module-foo module-bar module-late">
Module
</div>
</body>
</html>
在 <body>
中将 <link>
或 <style>
块作为子级实际上是无效的(尽管它可以工作),因此由 loadCSS 加载的样式表几乎不可能默认情况下不是获胜者。
此外,您可以指定一个 ID 来定位,以便您可以控制 CSS 顺序
<script id="loadcss">
// load a CSS file just before the script element containing this code
loadCSS("path/to/mystylesheet.css", document.getElementById("loadcss"));
</script>
关键 CSS 会变得奇怪吗?
您可能根本使用 loadCSS 的原因之一是,您有意尝试延迟加载样式表,因为您正在将 关键 CSS 注入到 <head>
中,以尝试将样式放入第一个数据包中并加快渲染速度。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
/* #1
Critical CSS chunk up here */
</style>
<script>
/* #2
Load the rest of the CSS
*/
</script>
</head>
<body>
<div class="module module-foo module-bar">
Module
</div>
</body>
</html>
关键 CSS 的做法是将 CSS 选择器向上移动到更高的块中。 #1 块。 顺序最低且最容易覆盖的块。 因此,理论上,是的,在仅应用关键 CSS 与 CSS 完全加载时,比较页面时,CSS 的应用可能会出现冲突/更改。 但样式表确实完全加载,并且位于关键 CSS 之后,因此最终将按预期进行。
您可能需要微调哪些内容进入关键 CSS,哪些内容不进入——以避免出现奇怪的样式更改闪动。
扩展会变得奇怪吗?
在预处理器中,它们会。
假设您想用变体来设置某样东西的样式
<div class="module module-variation">Module</div>
而预处理器代码(为了演示目的而超级简化)最终会变成这样
%variation {
background: orange;
}
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
.module-variation {
@extend %variation;
}
您可能会想……好吧,.module-variation
是*最后* 声明的选择器,所以它获胜,因此背景应该是橙色。 但它不是,因为扩展将选择器移动到它扩展的内容定义的位置,在本例中是在我们尝试覆盖的内容之前。 编译后的 CSS 是
.module-variation {
background: orange;
}
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
因此,background
实际上是 #ccc
,而不是我们想要的。 可能最简单的解决方法是使用特定性而不是源顺序。
原生扩展,如果它们 成为现实,可能会不那么令人困惑。
它是一个很傻的事情需要管理
没有人想考虑这个问题。 使用特定性来获胜的样式要容易得多。 但了解它是个好主意,因为不必要的特定性膨胀也很糟糕。
我们继续跳舞。
我喜欢这里是如何真正成为一篇专家文章。 我对教育很感兴趣,教过很多人 HTML+CSS,前几天我们讨论了这里解释事情的专家方式。
这是 CSS 的基础……级联等等。 这是向学习者解释的根本内容,即如果您稍后写下不同的样式,它就会覆盖之前的样式。
但一旦您深入研究,甚至尝试了 SASS、BEM 或其他东西,您就不再真正使用级联了。 这就是为什么有时您需要现实检查的原因。 当然,这里还提到了所有特殊情况。
使用 sass 或 bem 不会影响特定性——它仍然使用,伙计。
实际上,使用 BEM 会让您比特定性更多地使用优先级,因为它的主要目标是降低特定性。
对于关于
@extend
的最后一部分,还有另一个有用的 CSS 规范可以解决这个问题
CSS @apply 规则 https://tabatkins.github.io/specs/css-apply-rule/
我们现在可以在 Chrome Canary 中使用
@apply
了!这是一篇关于该主题的好文章
需要注意的是,嵌入式样式始终获胜!(当然前提是特定性相同)
我之前没有意识到预处理器会改变顺序。 很好知道!
您还记得那篇文章吗?它说“不要使用 background 而不是 background-color”。
这样做的思路是:它会重置您可能没有意图重置的内容(例如 background-repeat)。 重要的是要意识到这一点。
感谢作者
关于 body 中的链接和样式,请参见
https://jakearchibald.com/2016/link-in-body/
这是不鼓励的做法,但正如您在这里所说,它并不“无效”。 显然。
看起来 Chrome 将向 IE/Edge 行为迈进,其中将支持内容的渐进式渲染。 样式表链接将在下方阻止,但在上方不会阻止。
我所说的“无效”是基于目前 https://validator.w3.org/ 的说法,它可能不是最新的。
您好,这篇文章很好而且令人兴奋,但我对 CSS 编译过程中的顺序变化有疑问……如果您要扩展另一个类,会选择最上面的那个吗? 它是否尊重与扩展类顺序相关的顺序?
谢谢!
扩展占位符将始终将选择器放在占位符位置,因此选择器的数量无关紧要。 它将始终按 Chris 所解释的那样工作。