高级CSS选择器指南--第二部分

107 阅读10分钟

继续第一部分的内容,本集将重点介绍被归类为伪类和伪元素的高级CSS选择器,以及每种选择器的实际应用。我们将特别尝试理解nth-child 的语法。

伪类

这是最大的一个类别,也是最依赖上下文的一个类别。

伪类是一些关键词,当它们与元素的选定状态或上下文相匹配时就会被应用。

这大大增加了CSS的功能,并实现了在过去经常被错误地归入JavaScript的功能。

有些选择器是有状态的:

  • :focus
  • :hover
  • :visited
  • :target
  • :checked

而其他的选择器则与元素的顺序相联系:

  • :nth-child() /:nth-of-type()
  • :first-child /:first-of-type
  • :last-child /:last-of-type
  • :only-child /:only-of-type

然后是非常有用的伪类:not() ,新支持的:is() ,以及随着CSS自定义属性(变量)的支持率上升而出现的:root 伪类。

查看MDN文档,了解可用的伪类的完整列表,包括针对表单输入、video 字幕和语言的可用选项,以及一些目前正在实施的选项。

伪类的实际应用#

斑马纹表行

nth 系列的选择器有无穷的应用。将它们用于任何你想使用基于1的索引出现的重复模式。

一个很好的候选者是表行的斑马纹。

nth-child 选择器可以使用一个整数,用函数符号来定义,或者使用关键字evenodd 。我们将使用关键字来最有效地产生我们的斑马线规则。

tbody tr:nth-child(odd) {
  background-color: #ddd;
}

这将产生以下结果:

a two-column table where rows in the table body have a light grey background for every other (odd) table row background

应用交替的背景颜色

使用nth-child 的功能符号,我们可以交替使用一系列的背景颜色,并确保图案按照定义的顺序重复,无论存在多少个元素。因此,rebeccapurple,darkcyan,lightskyblue 的模式将按这个顺序重复。

这种工作方式是将颜色的总数--3 --与n 一起定义,它代表从0开始的所有正数,并将与相关数字相乘,在这种情况下,3 。所以就其本身而言,3n 会选择第3项、第6项、第9项,以此类推。它不会选择列表中的第一个,因为3 x 0 = 0

对于我们的重复模式,我们希望第一个被选中的项目是我们调色板中的第一个颜色。

因此,我们把这个符号扩展为3n + (integer corresponding to color order) ,因此我们的第一种颜色规则就变成了。

li:nth-child(3n + 1) {
  background-color: rebeccapurple;
}

而这将选择每三个元素,从第一个开始:

5 stacked items where the first and fourth have the background-color: rebeccapurple applied

本质上,这个+ [number] ,转移了起始索引。

为了完成我们的模式,我们定义了以下规则,增加的数字是重复模式中颜色的顺序:

li:nth-child(3n + 2) {
  background-color: darkcyan;
}

li:nth-child(3n + 3) {
  background-color: lightskyblue;
}

产生以下完成的结果。

删除子元素的额外间距

如果你没有使用以零边距开始所有元素的重置,你可能会遇到排版元素产生额外的不需要的边距,使视觉容器内的间距不平衡。

伪类并不总是需要直接附加到一个元素上,这意味着我们可以做下面的规则,它附加到任何元素,恰好是任何父元素的最后一个孩子,并确保它没有margin-bottom

:last-child {
  margin-bottom: 0;
}

从选择器中排除元素

仔细应用:not() ,对于排除被选择的元素非常有用。

我们在属性选择器部分探讨了:not() 的一些用途,特别是a:not([class]) ,用于针对没有应用其他类的链接。

:not() 是在实用框架或设计系统中使用的极好的方法,以提高对有可能应用于任何东西的类的特异性,对于这些类,在某些组合上存在着已知问题。

对有链接的类排除它的一个扩展例子是,当你调整文本的对比度时,可能是在黑暗模式的背景下,并希望将对比度调整也应用于文本链接。

/* Non dark mode application */
a:not([class]) {
  color: blue;
}

/* Update text color for dark mode */
.dark-mode {
  color: #fff;
}

/* Extend the color update to links via `inherit` */
.dark-mode a:not([class]) {
  color: inherit;
}

你也可以连锁:not() 选择器,所以也许你想为表单字段输入创建一个规则,但不包括某些类型。

input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"])

也可以在:not() ,包括其他伪选择器,如排除按钮的:disabled 状态。

button: not(: disabled);

这允许你通过先定义一个重置的button 样式来获得更整洁的规则,然后对非禁用的按钮应用颜色样式、边框等,而不是在以后删除这些样式用于button:disabled

有效地选择元素组

新支持的 :is() 伪类。

"......接受一个选择器列表作为其参数,并选择任何可以被该列表中的一个选择器选择的元素。" -MDN docs on :is()

这可以产生很大的影响的一种方式是更紧凑地选择排版元素,如:

:is(h1, h2, h3, h4) ;

或者更简洁地确定布局样式的范围,如:

:is(header, main, footer) ;

我们甚至可以将:is():not() ,并真正地精简我们的选择器,在这种情况下,选择不是标题的元素。

:not(:is(h1,h2,h3,h4))

要想了解这个选择器的背景,请查看ModernCSS配套项目SmolCSS.dev中的Smol Composable Card组件

在不久的将来,如果你想开始使用这个选择器,你至少要包括webkit 前缀版本。由于浏览器在使用选择器时的一个怪癖,你需要把它作为一个独立的规则,与is() ,以避免浏览器把这两个规则扔掉。

:-webkit-any(header, main, footer) ;

为当前锚定链接元素设置样式

当一个元素是锚定链接(文档片段标识符)的目标时--https://url.com/#anchor-here ,你可以使用:target

我的项目11ty.Rocks依赖于锚链接,例如可以看到访问这个链接的CSS Houdini Worklet Generator

:target 伪类应该放在包含id 属性的元素上。然而,你可以用下级选择器来影响嵌套元素--也许你想给article:target h2 一个更大的尺寸或类似的东西。

利用:target ,我通过与伪元素::before 结合,增加了一点额外的信息,以帮助向访问者表明他们被提供的链接的项目,显示如下("你要找的是我...")。

the :target style as applied to an article on 11ty.Rocks which has the message as described prior to this image

额外的提示:通过使用scroll-margin-top: 2em; (或其他你选择的值),确保在滚动时在元素顶部之前有一点间隔。这应该被认为是一个渐进式的增强,请务必查看浏览器对scroll-margin-top 的支持。

视觉上显示已访问的档案链接

:visited 伪类是非常独特的,因为它有可能在用户的隐私方面被利用。为了解决这个问题,浏览器制造商已经限制了哪些CSS样式允许使用:visited

Una Kravets有一个更深入的参考资料,探讨如何创建有用的:visited 样式,但这里是我为Style Stage的访问者提供的简化版本,以跟踪他们已经查看过的样式

一个关键的问题是,通过:visited 应用的样式将总是使用父级的alpha通道--这意味着,你不能使用rgba 来从不可见到可见,你必须改变整个颜色值。

所以,为了隐藏初始状态,你需要能够使用一个纯色,比如说页面背景色。

此外,为了无障碍性,如果是一个图标或表情符号,可能不希望伪元素内容被读出,因为我们不能为content 值提供一个无障碍的名称。在伪元素内容是否被读出方面,辅助技术之间存在不一致,所以我们可以尝试使用aria-hidden="true" ,确保它被忽略。

那么,我们的第一步是在链接中添加一个跨度,这就是我们最终要应用:visited 样式的地方:

<a href="#">Article Title <span aria-hidden="true"></span></a>

默认样式(非访问)添加了伪元素,并使其颜色与页面背景相同,以在视觉上隐藏它:

a span[aria-hidden]::after {
  content: "✔";
  color: var(--color-background);
}

然后,当链接被访问后,我们更新颜色,使其可见:

a:visited span[aria-hidden]::after {
  color: var(--color-primary);
}

先进的交互方式:focus-within

一个即将出现的伪类是:focus-within ,它有一个polyfill,但除此之外,应该谨慎使用,或作为一个渐进的增强。

MDN文档中描述的:focus-within 伪类

:focus-within CSS伪类代表一个已经收到焦点的元素或包含一个已经收到焦点的元素。换句话说,它代表一个本身被:focus 伪类匹配的元素,或者有一个被:focus 匹配的后裔。

伪元素#

伪元素允许你对所选元素的一个特定部分进行样式设计。它们的应用差别很大,(目前)最好的支持是以下几种:

  • ::after
  • ::before
  • ::first-letter
  • ::first-line
  • ::selection

伪元素的实际应用

额外的视觉元素的造型优势

::before::after 伪元素创建了一个额外的元素,在视觉上似乎是DOM的一部分,但并不是真正的HTML DOM的一部分。它们和任何真正的DOM元素一样,都是完全可以被样式化的。

我已经用这些元素做了各种点缀。由于它们的行为就像真正的元素,在使用flexbox或CSS网格布局时,它们被计算为子元素,这大大增加了它们在我工具箱中的功能。

使用::before::after 的几个关键概念。

  • 需要content 属性,然后才能使其可见,但这个属性可以设置为一个空白字符串 -content: "";
  • 关键的文本内容不应包括在content 值中,因为辅助技术对它的访问是不一致的
  • 除非另有定位,否则::before 将在主元素内容之前显示,而::after 将在其之后显示。

下面是一个只应用了一点样式的默认行为的演示:

A short paragraph showing the the text of "Before" in sitting prior to the paragraph content and the text of "After" sitting after the paragraph content

请注意,它们默认情况下就像内联元素,对于较长的内容也遵循包装行为:

A slightly longer paragraph that has wrapping lines showing the the text of "Before" in sitting prior to the paragraph content and the text of "After" sitting after the paragraph content

这里是通过单一的调整将display: flex ,以增加段落: The same multiline paragraph but the "Before" appears as a column to the left of the paragraph content, and "After" appears as a column after the paragraph content

这里是将其替换为display: grid

The same multiline paragraph but the "Before" appears as a row on top of the paragraph content, and "After" appears as a row below the paragraph content

::before::after 元素是添加简单、一致的排版装饰的快速方法,在这个 CodePen 演示中可以看到其中一些。

作者:Stephanie Eckles (@5t3ph)

你抓住了表情符号中的技巧吗?

我们可以通过attr() 函数检索元素上的任何属性的值,以便在content 属性中使用。

/*
<h2 class="emoji" data-emoji="😍">
*/
.emoji::before {
  content: attr(data-emoji);
}

这里有一个要点,就是如何用这个同样的思路在伪元素中显示元素idclass 的值你也可以在推特上分享这个提示 >

强调文章的线索

lede"(读作 "引子")是一个新闻术语,指的是新闻文章中的第一段,目的是对文章的关键点进行总结(你可能听说过 "不要埋没引子 "这句话)。

我们可以将:first-of-type 的伪类与:first-line 的伪元素结合起来,以强调段落副本的第一行。有趣的是,这是动态的,会随着视口大小的变化而变化。

article p:first-of-type:first-line {
  font-weight: bold;
  font-size: 1.1em;
  color: darkcyan;
}

产生以下固有的响应性结果:

gif demo of resizing the viewport when the previously described rule is applied and seeing that as the number of words in the first line of the paragraph changes the style continues to only affect the very first line

确保文本选择的可访问对比度

一个经常被忽略的样式是文本选择,尽管它是我们许多人每天多次参与的互动。

虽然浏览器试图处理这个事件的样式,但有可能失去对比度。我在为ModernCSS.dev设计样式时遇到了这种情况,因为使用的是深色的主题。

为了解决这个问题,我们可以使用::selection 伪元素来提供一个自定义文本颜色和背景颜色:

::selection {
  background: yellow;
  color: black;
}