高级 CSS 选择器指南 - 第一部分
这是系列文章的第 24 集,该系列文章探讨了现代 CSS 解决方案,以解决我过去 13 多年来作为前端开发者遇到的各种问题。访问ModernCSS.dev即可查看整个系列文章及其他资源。
无论您选择完全编写自己的 CSS,还是使用框架,或者需要在设计系统内构建 - 理解选择器、级联和特殊性对于开发 CSS 和修改现有样式规则都至关重要。
你可能非常熟悉基于 ID、类和元素类型创建 CSS 选择器。而且你可能经常使用不起眼的空格字符来选择后代元素。
在这个由两部分组成的迷你系列中,我们将探讨一些更高级的 CSS 选择器,以及何时使用它们的示例。
第一部分(本文):
第二部分:
CSS 特殊性和层叠
成功设置 CSS 选择器的一个关键概念是理解所谓的 CSS 特异性,以及 CSS 中的“C”,即级联。
特异性是应用于给定 CSS 声明的权重,由匹配选择器中每种选择器类型的数量决定。当多个声明具有相同的特异性时,CSS 中找到的最后一个声明将应用于该元素。特异性仅当同一元素被多个声明定位时才适用。根据 CSS 规则,直接定位的元素始终优先于元素从其祖先继承的规则。 - MDN 文档
!important
正确使用级联和选择器的特殊性意味着您应该能够完全避免在样式表中使用。
特异性的提高是由于覆盖级联继承的结果。
举一个小例子——它会.item
是什么颜色?
<div id="specific">
<span class="item">Item</span>
</div>
#specific .item {
color: red;
}
span.item {
color: green;
}
.item {
color: blue;
}
将会.item
是红色,因为在选择器中包含的特殊性#id
战胜了级联和元素选择器。
这并不意味着要添加#ids
所有元素和选择器,而是要注意它们对特异性的影响。
关键概念:特异性越高,规则越难被推翻。
每个项目在规则可复用性方面都有其独特的需求。共享低特异性规则的需求导致了 CSS 实用驱动框架(例如 Tailwind 和 Bulma)的兴起。
另一方面,在设计系统中,对继承和特异性的严格控制使得像 BEM 这样的命名约定变得流行起来。在这些系统中,父选择器与子选择器紧密耦合,从而创建可复用的组件,并创建自己的特异性气泡。
如果您认为“我不需要学习这些,因为我使用框架/设计系统”,那么您在充分利用 CSS 方面就受到了极大的限制。
该语言的美妙之处在于构建优雅的选择器,它可以完成足够的功能并支持整洁的小样式表。
通用选择器
通用选择器 - *
- 之所以如此命名,是因为它普遍适用于所有元素。
过去有人建议不要使用它,尤其是作为其后代,因为性能方面存在问题,但现在这不再是一个合理的考虑。事实上,十多年来它已经不再是一个问题了。与其为了性能而精打细算地使用 CSS 选择器,不如专注于 JS 包的大小并确保图片得到优化 😉
谨慎使用它的一个更好的理由是,当它单独使用时,它具有零特异性,这意味着它可以被单个 id、类或元素选择器覆盖。
通用选择器的实际应用
CSS 盒子模型重置
我最常见的用法是我的第一个 CSS 重置规则:
*,
*::before,
*::after {
box-sizing: border-box;
}
这意味着我们希望所有元素在计算盒模型时都包含内边距和边框,而不是将这些宽度添加到任何定义的尺寸中。例如,在以下规则中,元素的宽度.box
将是200px
宽,而不是200px + 20px
内边距:
.box {
width: 200px;
padding: 10px;
}
垂直节奏
Andy Bell 和 Heydon Pickering 在他们的Every Layout网站和书籍中推荐了另一个非常有用的应用程序,称为“ The Stack ”,其最简单的形式如下所示:
* + * {
margin-top: 1.5rem;
}
当与重置规则或将所有元素边距减小到零的父规则一起使用时,这会将上边距应用于所有跟随另一个元素的元素。这是获得垂直节奏的快速方法。
如果你确实想要更多一点 - 好吧,选择性 - 那么我喜欢在特定情况下使用它作为后代,例如:
article * + h2 {
margin-top: 4rem;
}
这与堆栈的想法类似,但更针对标题元素,以便在内容部分之间提供更多的喘息空间。
属性选择器
这是一个极其强大的类别,但往往没有充分发挥其潜力。
您是否知道可以通过利用 CSS 属性选择器来实现类似于正则表达式的匹配结果?
这对于修改 BEM 风格的系统或使用相关类名但可能没有单个公共类名的其他框架非常有用。
让我们看一个例子:
[class*="component_"]
此选择器将选择所有具有包含“component_”字符串的类的元素,这意味着它将匹配“component_title”和“component_content”。
i
并且您可以通过在关闭属性选择器之前包含以下内容来确保匹配不区分大小写:
[class*="component_" i]
但您不必指定属性值,您可以简单地检查它是否存在,例如:
a[class]
这将选择具有任何类值的所有(链接元素)。 a
查看 MDN 文档,了解在属性选择器中匹配值的所有可能方法。
属性选择器的实际应用
协助无障碍 Linting
这些选择器可用于执行一些基本的可访问性检查,例如:
img:not([alt]) {
outline: 2px solid red;
}
这将为所有不包含alt
属性的图像添加轮廓。
附加到 Aria 以强制可访问性
如果将属性选择器用作唯一选择器,它也能帮助增强可访问性,从而避免属性缺失导致相关样式无法使用。一种方法是将其附加到必需的aria
属性上
一个例子是,在实现手风琴交互时,您需要包含以下按钮,aria 布尔值是否通过 JavaScript 切换:
<button aria-expanded="false">Toggle</button>
然后,相关的 CSS 可以使用aria-expanded
作为属性选择器以及相邻的同级组合器来设置相关内容的打开或关闭样式:
button[aria-expanded="false"] + .content {
/* hidden styles */
}
button[aria-expanded="true"] + .content {
/* visible styles */
}
非按钮导航链接的样式
在处理导航时,您可能会混合使用默认链接和样式化为“按钮”的链接。在这种情况下,使用以下方法选择非“按钮”链接会非常有用:
nav a:not([class])
删除默认列表样式
我从 Andy Bell 和他的现代 CSS 重置中汲取的另一个技巧是,根据role
属性的存在删除列表样式:
/* Remove list styles on ul, ol elements with a list role,
which suggests default styling will be removed */
ul[role='list'],
ol[role='list'] {
list-style: none;
}
子组合器
子组合选择器 - >
- 在将样式应用于元素后代时,只需添加少量特异性即可有效缩小作用域。它是唯一一个处理元素层级的选择器,并且可以组合使用来选择嵌套元素。
子组合器将后代样式的范围从与子选择器匹配的任何后代限定为仅直接后代。
换句话说,虽然article p
选择的是文章内的所有内容 ,但仅选择文章内的段落,而不选择嵌套在其他元素中的段落。p
article
article > p
✅ 精选article > p
<article>
<p>Hello world</p>
</article>
🚫 未选择article > p
<article>
<blockquote>
<p>Hello world</p>
</blockquote>
</article>
子组合器的实际应用
嵌套导航列表链接
考虑一个侧边栏导航列表,例如一个文档站点,其中存在嵌套的链接层级。从语义上讲,这意味着一个外部链接ul
和一个嵌套的ul
内部链接li
。
为了实现视觉层次结构,您可能希望顶级链接的样式与嵌套链接不同。要仅定位顶级链接,您可以使用以下命令:
nav > ul > li > a {
font-weight: bold;
}
这里有一个 CodePen,您可以在其中试验如果删除该选择器中的任何子组合器会发生什么。
限定元素选择器的范围
我喜欢在页面布局中使用元素选择器来处理基础内容,例如header
或footer
。但你可能会遇到麻烦,因为这些元素是某些其他元素的有效子元素,例如或footer
中的元素。blockquote
article
在这种情况下,您可能需要从 调整footer
为body > footer
。
嵌入/第三方内容的样式
有时你确实无法控制类名、ID 甚至标记。例如,对于广告或其他 JavaScript 驱动(非 iframe)的内容。
在这种情况下,您可能会面临大量的 div 或 span,在这种情况下,子组合器对于将样式附加到不同级别的内容非常有用。
请注意,讨论的许多其他选择器也可以在这种情况下提供帮助,但只有子组合器处理级别并能影响嵌套元素。
通用兄弟组合器
通用同级组合器 - ~
- 选择位于前一个(先前定义的)元素之后某处且位于同一父级内的定义元素。
例如,将对位于段落之后某处p ~ img
的所有图像设置样式,前提是它们共享同一个父图像。
这意味着将选择以下所有图像:
<article>
<p>Paragraph</p>
<h2>Headline 2</h2>
<img src="img.png" alt="Image" />
<h3>Headline 3</h3>
<img src="img.png" alt="Image" />
</article>
但在这种情况下图像不是这样的:
<article>
<img src="img.png" alt="Image" />
<p>Paragraph</p>
</article>
您可能想要更具体一点(另请参阅:相邻兄弟组合器),并且此选择器往往在创造性编码练习中使用最多,例如我的CommitSweeper 纯 CSS 游戏。
通用兄弟组合器的实际应用
状态变化的视觉指示
将通用同级组合器与有状态伪类选择器(例如:checked
)相结合,可以产生有趣的结果。
假设复选框的 HTML 代码如下:
<input id="terms" type="checkbox">
<label for="terms">I accept the terms</label>
<!-- series of <p> with the terms content -->
仅当选中复选框时,我们才能使用通用同级组合器来改变术语段落的样式:
#terms:checked ~ p {
font-style: italic;
color: #797979;
}
低特异性变异
如果我们还引入通用选择器,我们可以快速生成细微的变化,例如简单的卡片布局。
我们可以使用通用兄弟组合器来产生以下变化,而不是使用类在嵌套的 div 中移动内容来改变标题和段落的排列。
该规则增加了一些边距,减小了字体大小,并减轻了图像后面任何元素的文本颜色:
img ~ * {
font-size: .9rem;
color: #797979;
margin: 1rem 1rem 0;
}
该规则的特殊性极低,因此您可以通过添加更有针对性的规则轻松地覆盖它。
此外,由于该规则仅适用于元素是图像父元素(li
在本例中为 )的直接后代时,一旦将内容包装在另一个元素中,该规则将仅在子元素使用继承之前适用。为了更好地理解这一点,请尝试将内容包装在 div 中的最后一个卡片项目中。颜色和边距将在div
和 type 元素上继承,但 上的原生浏览器样式h3
会阻止font-size
从通用同级组合器继承 ,因为原生浏览器规则的特异性比技术上针对 的通用选择器更高div
。
相邻兄弟组合器
相邻兄弟组合器 - +
- 选择紧跟在前一个(先前定义的)元素之后的元素。
我们已经在通用选择器示例中使用过它 - * + *
- 将顶部边距仅应用于跟随另一个元素的元素 - 所以让我们直接了解更多示例!
相邻兄弟组合器的实际应用
修复导航中 Flexbox 间隙支持不足的问题
Flexbox 间隙支持正在开发中,但在撰写本文时,Safari 的技术预览版之外尚不支持该功能。
因此,在网站导航等弹性布局非常有用的情况下,我们可以使用相邻的兄弟组合器来帮助添加边距作为的 polyfill gap
。
nav ul li + li {
margin-left: 2rem;
}
这样就可以在列表项之间产生间隙效果,而无需清除第一个列表项上的额外边距:
在表单标签和输入之间应用间距
我们为通用选择器探索的“堆栈”中应用的理论是仅在一个方向上应用边距。
我们可以使用这种想法来限定字段对象的作用域,以提供足够的间距来保持字段类型之间的视觉层次。在这种情况下,我们在标签和输入框之间添加一个较小的边距,并在输入框和标签之间添加一个较大的边距:
label + input {
margin-top: .25rem;
}
input + label {
margin-top: 2rem;
}
注意:此示例在有限的上下文中有效。您可能希望使用分组元素将字段类型括起来,以确保字段类型之间的一致性,而不是列出除 和 之外的所有字段类型input
。select
如果textarea
您对表单设计感兴趣,请查看 ModernCSS 上的迷你系列,并继续关注我即将推出的关于跨浏览器表单字段样式的Egghead 课程。
文章来源:https://dev.to/5t3ph/guide-to-advanced-css-selectors-part-one-3j51继续学习第二部分,我们将学习伪类和伪元素。我的另一个项目Style Stage是一个练习新学到的选择器知识的好地方,它是一个由社区贡献的现代 CSS 样式展示平台。如果你从本指南中学到了一些东西,并且有能力,我将非常感激你送我一杯咖啡,以便我为你带来更多教程和资源!