在整个项目中,保持 CSS 特定性 较低是一个值得的目标。它通常表明各个元素之间处于相对和谐的状态。您不会与自己作斗争,而且在需要时有足够的余地覆盖样式。选择器上的特定性往往会随着时间的推移而增加,而且存在一个硬性上限。我相信我们都经历过 !important 标签和内联样式的痛苦。
那么,我们如何才能随着时间的推移保持特定性较低呢?
给自己需要的类
也许您正在编写一个特定性很高的选择器,因为您正在覆盖一个已经存在的选择器。也许您要选择的元素包含在多个页面上,因此您从一个较高层的类中选择它
.section-header {
/* normal styles */
}
body.about-page .section-header {
/* override with higher specificity */
}
无论您对此有何感受,都要认识到特定性确实在这里逐渐增加。为了防止这种情况,您是否可以更改要直接设置样式的元素的类名?有时,创建一些服务器端辅助函数或变量来发出类可以很有帮助,以避免视图中的逻辑。
<header class="<%= header_class %>">
Which could output one class, or both, as desired.
</header>
.section-header {
/* normal styles */
}
.about-section-header {
/* override with same specificity */
/* possibly extend the standard class */
}
考虑样式表的源代码顺序
在尝试覆盖某些内容的相同情况下,也许您正在添加一个额外的类名来处理样式覆盖。
<header class="section-header section-header-about">
</header>
但是,您使用 .section-header-about
编写覆盖样式的位置实际上正在被现有类覆盖。这可能是因为样式表中的选择器顺序。这两个选择器具有完全相同的特定性,因此最后声明的规则获胜。
解决此问题意味着只要确保编写覆盖类的任何位置都位于后面。也许以某种方式组织您的样式表,例如
@import "third-party-and-libs-and-stuff";
@import "global-stuff";
@import "section-stuff";
@import "specific-things-and-potential-overrides";
降低要覆盖的元素的特定性
您知道关于将事物做得比您找到时更好的说法。您应该这样做。
如果某个元素上有一个 ID,并且这是它的样式来源,您可以将其删除吗?在执行此操作之前,一定要在整个项目中搜索 #that。它可能用作 JS 挂钩(完全没问题),在这种情况下,您应该保留它,并为 CSS 添加一个类或使用该元素上已有的类。
一开始不要避免使用类
我之前一直使用类似这样的选择器
.module > h2 {
}
这将正常工作,直到您想为该 h2 元素设置特殊样式的那一天。您可能会在标记中进行操作,并说
<div class="module">
<h2 class="unique">
Special Header
</h2>
</div>
但不幸的是
.module > h2 {
/* normal styles */
}
.unique {
/* I'm going to lose this specificity battle */
}
.module .unique {
/* This will work, but specificity creep! */
}
特定性逐渐增加,因为最初的选择器正在困扰着我们。这就是几乎所有 CSS 方法都建议使用类似以下内容的扁平结构的原因
<div class="module">
<h2 class="module-header">
</h2>
<div class="module-content">
</div>
</div>
从一开始就感觉像是更多繁琐的工作,因为您可能并不立即需要这些类。但是,通过不回避这项工作(不要偷懒!),您以后拥有的挂钩可以避免您的痛苦。
使用层叠
随着项目的发展,使用特定性*低*的选择器来修改选择器变得越来越危险,因为它们可能影响更多内容并产生意想不到的后果。
#im #just .gonna[do] .this .to .be #safe {
/* cries (ಥ_ʖಥ) */
}
但是,影响更多内容是 CSS 的强大功能。拥有一个坚实的构建基础意味着可能需要更少的覆盖。实现这一点的策略可以包括以下内容……
使用模式库和/或样式指南和/或原子设计
模式库(类似 这样)意味着您在需要时就可以找到所需的内容。需要一些选项卡吗?来吧,这是已建立的方法。而且它很可能以特定性很低的方式构建,因此覆盖不会太难。
原子设计(书籍)可以指导您构建网站(或模式库),因此,即使您没有完整的模式来满足您的需求,您也有其下方的构建模块。
一个 样式指南可能会帮助您,因为它可能会强制执行有关特定性的特定规则,以防止您自食其果。
考虑选择性排版
在尝试使用层叠并为大量元素设置智能默认值的同时,您可能希望有时对一些智能默认值进行范围限定。例如,列表元素通常用于导航和内容中。它们样式将大不相同。那么为什么不从空白状态开始,并在需要时才应用文本样式 呢。
/* reset styles globally */
ul, ol, li {
list-style: none;
margin: 0;
padding: 0;
}
/* scope text styles to text content */
.text-content {
h1, h2, h3 {
}
p {
}
ul {
}
ol {
}
/* etc */
}
这确实会提高这些文本选择器的特定性,但这意味着专门针对文本的规则不会影响比需要的影响更多的内容,并且减少了覆盖的需求。
超出控制范围的问题
也许某些第三方代码期望或将某些 HTML 注入您的页面。您必须处理这些问题。您仍然可以尝试使用特定性尽可能低的
仅稍微提高特定性并进行记录
如果您需要进行特定性争斗,而不是使用像 ID 或 !important 这样的重型武器,请尝试更轻的东西。
标签限定符是特定性升级的最小可能性。
ul.override {
/* I win */
}
.normal {
}
但是,将选择器限制为特定标签是一个奇怪的限制因素。最好添加一个额外的类。如果另一个名称没有意义,您甚至可以根据需要使用相同的类。
.nav.override {
}
.override.override {
}
.nav {
}
仅仅因为嵌套很好,并不意味着特定性必须提高
预处理器中的嵌套有时不被鼓励,因为它使编写过于复杂的选择器变得非常容易。但嵌套有时在样式表中只是视觉上的好处,因为它将事物分组在一起,使其更易于阅读和理解。
Bohdan Kokotko 最近提醒我,Sass 中的 & 可以用来做本质上的命名空间,而不是复合选择器。
.what {
color: red;
&-so {
color: green;
&-ever {
color: blue;
}
}
}
.what {
color: red;
}
.what-so {
color: green;
}
.what-so-ever {
color: blue;
}
单类包装器
处理覆盖的一种比较强硬的方式是在需要应用样式覆盖的区域使用现有的包装元素,或者添加一个新的包装元素。
<div class="override">
... existing stuff ...
</div>
现在您可以通过在选择器中添加一个类来覆盖任何现有的样式。这是特异性蠕变,但试图保持特异性较低。
.override .existing {
}
只此一次
如果要覆盖一个覆盖,那应该是一个停止并重新考虑的时机。
您可以给自己一个可以使用的一次性类吗?一次性覆盖实际上可以很高效,就像是对模式的变体,而不是创建新的模式。覆盖一个覆盖的做法会带来混乱和进一步的冲突。
下次做得更好
如果您遇到特异性问题,即使现在无法解决,至少您现在知道它有問題,并且下次可以采用不同的方法。
我在我的 Sass 中大量使用和符号,结合 BEM 使得将特异性保持在最低限度变得容易得多,至少对我来说,它也使可读性更强,因为子元素会嵌套显示。我倾向于更多地使用源代码顺序进行覆盖。当然,并非所有情况都适用,但我认为这是一个总体的良好目标。
正如 Harry Roberts 也提到的那样,样式表中的特异性应该逐渐增加,这反过来也使得管理特异性变得更加直观,尤其是在长期项目中。
不错,以前不知道它可以这样做 oO。不过,您也可以在线将其他选择器与和符号结合起来。
&-class
方法的权衡是,您会失去给定 IDE 的查找功能。因此,当团队中的新人或从未接触过您源代码这部分的人尝试使用.element-sub-sub-name
调试给定模块,并且查找功能返回 0 个结果时,最佳做法是提供源代码映射,帮助追踪这些实例并进行调试。这是一个有效的观点。但我认为,如果您的代码非常模块化,每个模块都分成单独的命名恰当的文件,并且您的 BEM 嵌套深度不超过 3 级,那么您应该不会很难找到他们想要找的东西。
我倾向于这样评论每个代码块
这样您就可以使用 #BLOCK-NAME 搜索整个项目以定位该文件,在大多数情况下,您可以快速看到后代。如果模块非常复杂,那么您可以始终添加一些描述。
这可能被认为是过度设计,我仍在试验中,但到目前为止,它一直对我很有用,但我始终乐于接受其他建议。
Chris,又一篇很棒的文章。感谢。我特别喜欢 Sass 命名空间,以前从未见过,现在打算试一试。
我很担心看到服务器端逻辑构建 HTML 的类列表,因为它将前端和服务器代码更紧密地耦合在一起。这似乎打破了关注点分离,因为服务器逻辑会影响样式和呈现。
也就是说,我完全理解为什么在现有项目中这可能是最简单的解决方案,因为您列出的其他选项由于某种原因不起作用。
另一个有用的注意事项是,您可以提高特异性,而无需添加额外的类或限定选择器(如果元素发生更改怎么办?
我们只需将相同的类链接起来。
所以
可以选择成这样
在这种情况下,它将具有 3 个类选择器的选择器权重。
在大多数情况下,我会尽量避免这种情况,但会有一些情况它会成为合理的解决方案。
抱歉,Chris 在原始帖子中已经提到了这一点。但它仍然很酷 :)
这是一个非常有趣的技巧。我不确定哪些情况下需要这样做——您是否遇到过需要这样做的情况?
并不常见,但如果与该类名存在特异性冲突,您可以简单地提高特异性,而无需在标记中添加额外的类名,或将其绑定到特定元素。
很棒的文章,我以前从未见过 .selector.selector 这种方式来略微提高特异性。
我喜欢这样的想法
我最近开始使用双斜杠来表示用于修改现有类的类,现有类在双斜杠之前。例如
这使得修改类的相关类非常清楚,即使它们在样式表中被分开了,并且可以保持特异性较低。
Chris,一如既往的好主意!
有一点题外话,关于源代码顺序,我不明白为什么很难从像 Google Chrome 这样的浏览器中确定样式表的实际处理顺序。显然,您可以直接查看源代码,但通常在大型项目中,会有很多单独的样式表。
我看到的唯一方法是使用在控制台中运行的代码段
感谢 Chris。一如既往,很棒的文章 :)
这个网站 - http://cssstats.com/ - 创建了一个关于代码特异性的漂亮图表(以及其他内容)。
这是一个很酷的工具。感谢提醒!
它很丑陋,但使用属性选择器而不是类名或 ID 也可以降低特异性。
更正:属性选择器和类选择器具有相同的特异性 (010)。您提到的技巧仅适用于使用 ID 进行选择。
Chris,很棒的文章!
我想知道有多少人会发现这些方法非常有用。
感谢您的提及,这对我意义重大;)
CSS 框架可能会(这可能更多归因于人们的使用方式而不是框架本身)过度担心 CSS 中的层叠部分。“特异性太难管理了,所以我们要放弃所有内容并使用单个类并将 DOM 嵌入到类名中。” 使用 SASS(和 Less),由于
&extend
,您的源代码可以相对 DRY,并且压缩将抹去大部分额外的字节。我的问题是,我们往往没有看到问题的全部。
部分问题在于,我们将太多属性组合到同一个选择器中。以
.module > h2
为例。在上面设置 padding 和 margin 是完全合理的,因为这将是模块的责任。在这种情况下,使用.module-h2
或类似的名称并没有什么帮助。如果你正在设置颜色和字体之类的东西,你可能在那个选择器中做了太多事情,你真正想要的是
.default-story-heading
或类似的东西,然后是.unique-story-heading
。在这些类中设置填充和边距可能是不合适的,除非你打算它只在模块中使用,在这种情况下.module > .unique
是**正确的**,因为它可以防止有人无意中在你不打算的地方使用它。或者如果你打算在多个地方使用它,但当它在模块中时也更改其位置(或者其他什么),那么两个选择器(.unique
和.module > .unique
)是合适的,只需将常见的内容(颜色、字体等)放在第一个选择器中,将特定内容(填充、边距)放在第二个选择器中。我们也过于频繁地使用简写属性。你很少会设置左侧填充而不关心顶部填充,但我无法计算出我发现有人使用
background: red
并删除了我想要显示的png的次数,所以现在我必须绕过它。所以,**是的**,我们需要注意特异性,只有在必要时才提高它。我们也需要将属性分解成多个具有适当特异性的选择器。我们还需要注意只设置我们打算设置的属性,而不是其他任何属性。
错误的。
@Adam - 完全正确。+1
一些可靠的技巧,感谢您将它们整理在一起。
@at-root 是另一个你可以用来将嵌套规则拉回到选择器根部的工具。这可以保持低特异性,同时提供在 Sass 文件中看到嵌套层次结构的视觉优势。
.nested { @at-root { .nested-override { … } } } 编译为
.nested {} 和
.nested-override { … }
例如
这太具体了
这更好
哦,抱歉。请删除/忽略该
nav ul li a:hover {}
行。然后,要给锚点着色,不要这样做
但
它并不更好。例如,如果我想在
nav ul
之外再次使用nav ul {}
中定义的列表类型,我将不得不复制和粘贴该代码或在逗号后添加另一个选择器。它会创建重复的 CSS 或过于耦合。这只是一个简单导航标记的示例。这取决于您的需求。您可以自由为每个列表指定更具体的类。
注意:我不知道包含多个直接列表元素子节点的
<nav>
是否有效。非常有用的建议!碰巧我正在工作中进行一项练习,以减轻样式表的重量,我正在删除当前样式表中的许多选择器。
我无法更改 HTML,因此只能使用现有的 HTML。这意味着有时我必须使用 ID 或 2 个类加上元素选择器,但只要有可能,我都会尝试只使用单个类。我还在查看元素在页面上的位置,并根据 HTML 文档重新排序代码;这样,我知道我可以在文档中使用更通用的选择器,并将更具体的选择器保存到实际需要的时候。
这实际上是一项有趣的练习;使用大型网站(例如企业级电子商务)的 CSS 并将其重写以降低特异性或文件大小。它让我想要学习 SASS 或 LESS,并且它向我展示了如何编写经济的代码。以前,在较小的网站中,我会更乐意使用某个选择器,但现在我开始思考“这个选择器以后会带来什么问题?”
非常有用的信息,感谢分享。
我最近写了关于特异性图表的文章 http://josephrex.me/specificity-wars/。在像 WordPress 这样的平台上,当插件与主题样式规则发生冲突时,它确实会变成一场战争。我刚刚完成的博客设计在这里有一个相当不错的图表:https://gist.github.com/bl4ckdu5t/9bda2e463d96f75d7932
这是一篇发人深省的博客文章,Chris,不过它让我想到了我似乎遇到的以下难题
我个人一直在我的 CSS 中使用很多嵌套关系。这样做的原因是为了避免修改 HTML“表示层”,就像我们以前那样避免使用 JavaScript。
因为这篇博客文章,我有一个很大的问题,那就是在你的文档中使用更通用的类,难道不会通过 HTML 和 CSS 中的类命名系统显着增加 HTML 页面重量吗?例如
使用嵌套 CSS 关系的方式最终会产生更少的代码吗?例如
- 或甚至 -
它让我思考了很多关于如何让我们的代码可扩展,以及如何将可扩展性与简化和性能考虑因素联系起来,我只是想知道你对此类差异的想法是什么?
我会将你指向 SMACSS 的模块规则 https://smacss.com/book/type-module
FYI,你的示例之一似乎有问题。在“单类包装器”部分,似乎有一部分 HTML 被渲染为……现有内容……而不是显示代码。
很棒的文章,Chris!我不知道 Sass 的命名空间功能,也没想过在选择器中复制相同类名的技巧。狡猾。:-)
我们使用遵循 HTML 层次结构的 CSS 命名方案取得了良好的效果,该方案将 CSS/HTML 分为已实现的组件、子组件……等等。
所有 Aimeos 网店组件都由 CSS 类“.aimeos”覆盖,单个组件以代码部分(“.catalog-filter”)命名,子组件更具体(“.catalog-filter-tree”)
你可以在 https://github.com/aimeos/arcavias-core/blob/master/client/html/themes/elegance/aimeos.css 找到完整的 CSS
唯一会导致问题的是,如果你的 CSS 类过于不具体,例如“.container”、“.name”等。它们很可能在其他地方也被定义。
嗨,Chris,我认为你在“预处理器中的嵌套有时不被鼓励,因为它很容易编写过于选择器”中有一个拼写错误 - 你的意思是“预处理器中的嵌套有时不被鼓励,因为它很容易编写过于[具体]的选择器”吗?
+1 用于使用“&”符号,它与 BEM 符号非常有效。
非常有见地的文章,Chris,我喜欢“选择性排版”的概念……
很棒的文章!另一种开发 CSS 的有趣方法是使用属性模块 CSS。你可以在此处查看它:http://amcss.github.io/
抱歉,但我不同意某些观点。我更喜欢我的 HTML 干净,会在需要时添加类,否则会使用 CSS 来完成工作。以下示例在我看来是正确的。
.module > h2 {
/* 正常样式 /
}
.module .unique {
/ 这就是 CSS 应该级联的方式 */
}
我同意。我在编写 HTML 时不会考虑设计。然后我使用 CSS 来设置样式。你仍然可以在不为所有内容添加类的同时,拥有健全的 CSS 代码结构。
很棒的文章,Chris!
嗯,这难道不会让预处理器和嵌套变得有点多余了吗?
你给出的
.module > h2
示例的另一个问题是,它将 CSS 紧密地耦合到 HTML 的流程。为什么不好?假设你想在
.module
和h2
之间引入一个新元素,现在我放在其中的任何东西都会
现在已经消失了,除非你重写选择器。我们过去在我的公司里这样做,认为我们用新的 HTML5 选择器编写“语义化”HTML 和 CSS 很酷,但我们很快就遇到了这个问题。
我真的很喜欢这篇文章。我自己也尽量坚持使用非特定的 CSS 编写风格,但不幸的是,当外部供应商代码被混合进来时,事情就变得复杂了,这时事情往往变得过于具体。