新支持的现代 CSS 伪类选择器指南

已发表: 2022-03-10
快速总结 ↬ CSS 工作组编辑器草案第 4 级选择器包括几个伪类选择器,这些选择器已经在大多数现代浏览器中具有候选提案。 本指南将涵盖目前获得最佳支持的指南以及示例,以展示您今天如何开始使用它们!

伪类选择器是以冒号“ : ”开头并根据当前元素的状态进行匹配的选择器。 状态可能与文档树相关,或者响应状态变化,例如:hover:checked

:any-link

尽管在 Selectors Level 4 中定义,但这个伪类已经支持跨浏览器很长一段时间了。 any-link伪类将匹配一个锚超链接,只要它有一个href 。 它将以等同于同时匹配:link:visited的方式匹配。 从本质上讲,如果您要添加基本属性(例如color ),您希望将这些属性应用于所有链接,而不管它们的访问状态如何,这可能会使您的样式减少一个选择器。

 :any-link { color: blue; text-underline-offset: 0.05em; }

关于特异性的一个重要注意事项是:any-link作为选择器将胜过a ,即使a在级联中的位置较低,因为它具有类的特异性。 在以下示例中,链接将为紫色:

 :any-link { color: purple; } a { color: red; }

因此,如果您引入:any-link ,请注意,如果它们将直接竞争特异性,则需要将其包含在a的实例中作为选择器。

:focus-visible

我敢打赌,网络上最常见的可访问性违规之一是删除交互元素的outline ,如链接、按钮和表单输入的:focus状态。 该outline的主要目的之一是为主要使用键盘导航的用户提供视觉指示器。 可见的焦点状态作为一种寻路工具至关重要,因为这些用户在界面上进行选项卡并帮助加强什么是交互式元素。 具体来说,WCAG 成功标准 2.4.11:焦点外观(最低)中涵盖了可见焦点。

:focus-visible伪类旨在仅在用户代理通过启发式方法确定它应该可见时才显示焦点环。 换句话说:浏览器将根据输入法、元素类型和交互上下文等因素确定何时应用:focus-visible 。 出于测试目的,通过带有键盘和鼠标输入的台式计算机,您应该看到:focus-visible样式在您进入交互式元素时附加,但在单击它时不会看到,除了文本输入和应该显示:focus-visible的文本区域对所有焦点输入类型:focus-visible

注意有关更多详细信息,请查看:focus-visible规范的工作草案。

根据规范,最新版本的 Firefox 和 Chromium 浏览器现在似乎正在处理表单输入上的:focus-visible ,该规范说,当:focus-visible匹配时,UA 应该删除:focus样式。 Safari 尚不支持:focus-visible ,因此我们需要确保包含:focus样式作为后备,以避免删除可访问性的outline

跳跃后更多! 继续往下看↓

给定具有以下样式集的按钮和文本输入,让我们看看会发生什么:

 input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }

铬和火狐

  • input
    当元素通过鼠标输入聚焦时正确删除:focus样式以支持:focus-visible导致更改border-color并隐藏键盘输入上的outline
  • button
    不仅使用:focus-visible没有额外的button:focus:not(:focus-visible)规则删除:focus上的轮廓,而且只允许在键盘输入上看到box-shadow

苹果浏览器

  • input
    继续仅使用:focus样式
  • button
    现在,这似乎部分尊重了按钮上:focus-visible的意图,方法是在点击时隐藏:focus样式,但仍会在键盘交互时显示:focus样式

所以现在,建议继续包括:focus样式,然后逐步增强到使用演示代码允许的:focus-visible 。 这是一个 CodePen 供您继续测试:

请参阅 Stephanie Eckles 的 Pen [Testing application of :focus-visible](https://codepen.io/smashingmag/pen/MWJZbew)。

请参阅 Stephanie Eckles 的 :focus-visible 笔测试应用程序。

:focus-within

:focus-within伪类在所有现代浏览器中都有支持,并且几乎就像父选择器一样,但仅适用于非常特定的条件。 当附加到包含元素并且子元素匹配:focus时,可以将样式添加到包含元素和/或容器内的任何其他元素。

使用此行为的一个实际增强功能是在关联输入具有焦点时设置表单标签的样式。 为此,我们将标签和输入包装在一个容器中,然后将:focus-within附加到该容器并选择标签:

 .form-group:focus-within label { color: blue; }

当输入具有焦点时,这会导致标签变为蓝色。

此 CodePen 演示还包括直接向.form-group容器添加大纲:

请参阅 Stephanie Eckles 的 Pen [Testing application of :focus-within](https://codepen.io/smashingmag/pen/xxgmREq)。

请参阅 Stephanie Eckles 的 :focus-within 笔测试应用程序。

:is()

也称为“匹配任何”伪类, :is()可以获取选择器列表来尝试匹配。 例如,您可以将它们分组在:is(h1, h2, h3)的选择器下,而不是单独列出标题样式。

关于:is()选择器列表的一些独特行为:

  • 如果列出的选择器无效,则规则将继续匹配有效的选择器。 给定:is(-ua-invalid, article, p)规则将匹配articlep
  • 计算出的特异性将等于具有最高特异性的传递选择器的特异性。 例如, :is(#id, p)将具有#id的特异性 - 1.0.0 - 而:is(p, a)将具有 0.0.1 的特异性。

忽略无效选择器的第一个行为是一个关键的好处。 当在一个选择器无效的组中使用其他选择器时,浏览器将抛出整个规则。 这在一些仍然需要供应商前缀的情况下发挥作用,并且对前缀和非前缀选择器进行分组会导致规则在所有浏览器中失败。 使用:is()您可以安全地对这些样式进行分组,它们将在匹配时应用,而在不匹配时被忽略。

对我来说,前面提到的对标题样式进行分组已经是这个选择器的一大胜利。 这也是我在应用非关键样式时使用起来很舒服的规则类型,例如:

 :is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }

在这个例子中(来自我的项目 SmolCSS 中的文档样式),从基本样式继承的更大的line-height或缺少margin-top对于不支持的浏览器来说并不是真正的问题。 这根本不够理想。 您暂时还不想使用:is()的是关键的布局样式,例如 Grid 或 Flex,它们可以显着控制您的界面。

此外,当链接到另一个选择器时,您可以测试基本选择器是否与:is()中的后代选择器匹配。 例如,以下规则仅选择文章的直接后代段落。 通用选择器被用作对p基本选择器的引用。

 p:is(article > *)

为了获得当前的最佳支持,如果您想开始使用它,您还需要通过使用:-webkit-any():matches()包含重复规则来加倍样式。 请记住制定这些单独的规则,否则即使支持的浏览器也会将其丢弃! 换句话说,包括以下所有内容:

 :matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }

在这一点上值得一提的是,与较新的选择器本身一起是@supports的更新变体,即@supports selector 。 这也可用作@supports not selector

注意目前(现代浏览器),只有 Safari 不支持此规则。

您可以使用以下内容检查:is()支持,但实际上您将失去对 Safari 的支持,因为 Safari 支持:is()但不支持@supports selector

 @supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }

:where()

伪类:where()几乎与:is()相同,除了一个关键区别:它总是具有零特异性。 这对于正在构建框架、主题和设计系统的人们来说具有不可思议的意义。 使用:where() ,作者可以设置默认值,下游开发人员可以包括覆盖或扩展而不会发生特异性冲突。

考虑以下一组img样式。 使用:where() ,即使使用更高的特异性选择器,特异性仍然为零。 在以下示例中,您认为图像将具有哪种颜色的边框?

 :where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }

第一条规则的特异性为零,因为它完全包含在:where()中。 所以直接反对第二条规则,第二条规则获胜。 引入img仅元素选择器作为最后一条规则,由于级联,它将获胜。 这是因为它将计算与:where(article) img规则相同的特异性,因为:where()部分不会增加特异性。

由于零特异性功能,使用:where()和回退有点困难,因为该功能可能是您希望:is()上使用它的原因。 而且,如果您添加回退规则,由于其本质,这些规则可能会击败:where() 。 而且,它比@supports selector具有更好的整体支持,因此尝试使用它来制作后备不太可能提供太多(如果有的话)收益。 基本上,请注意无法为:where()正确创建回退,并仔细检查您自己的数据以确定开始为您的独特受众使用是否安全。

您可以使用上面使用img选择器的以下 CodePen 进一步测试:where()

请参阅 Stephanie Eckles 的 Pen [Testing `:where()` specificity](https://codepen.io/smashingmag/pen/jOyXVMg)。

请参阅 Stephanie Eckles 的 Pen Testing :where() specificity。

增强:not()

从 Internet Explorer 9 开始支持基本的:not()选择器。但是选择器级别 4 通过允许它采用选择器列表来增强:not() ,就像:is():where()一样。

以下规则在支持浏览器时提供相同的结果:

 article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }

:not()接受选择器列表的能力具有很好的现代浏览器支持。

正如我们在:is()中看到的,增强的:not()还可以包含对基选择器的引用作为使用*的后代。 这个 CodePen 通过选择不是nav后代的链接来展示这种能力。

请参阅 Stephanie Eckles 的 Pen [Testing :not() with a descendent selector](https://codepen.io/smashingmag/pen/BapvQQv)。

请参阅 Stephanie Eckles 的 Pen Testing :not() 与后代选择器。

奖励:之前的演示还包括一个链接:not():is()的示例,以选择不是h2h3元素的相邻兄弟的图像。

提议但“有风险”—— :has()

最后一个伪类是一个非常令人兴奋的提议,但即使以实验方式也没有当前的浏览器实现它是:has() 。 事实上,它在 Selector Level 4 Editor's Draft 中被列为“有风险”,这意味着它被认为难以完成其实施,因此可能会从推荐中删除。

如果实现, :has()本质上将是许多 CSS 人员渴望拥有的“父选择器”。 它将使用类似于:focus-within:is()与后代选择器的组合的逻辑,您正在寻找后代的存在,但应用的样式将针对父元素。

给定以下规则,如果导航包含按钮,则​​导航将减少顶部和底部填充:

 nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }

同样,这目前还没有在任何浏览器中实现,甚至是实验性的——但想想很有趣! Robin Rendle 在 CSS-Tricks 上为这个未来的选择器提供了额外的见解。

3 级荣誉奖:empty

您可能在 Selectors Level 3 中错过的一个有用的伪类是:empty ,它在元素没有子元素(包括文本节点)时匹配该元素。

规则p:empty将匹配<p></p>但不匹配 < <p>Hello</p>

您可以使用:empty的一种方法是隐藏可能是使用 JavaScript 填充的动态内容的占位符的元素。 也许您有一个 div 将接收搜索结果,当它被填充时,它将有一个边框和一些填充。 但是还没有结果,您不希望它占用页面上的空间。 使用:empty你可以隐藏它:

 .search-results:empty { display: none; }

您可能正在考虑在空状态下添加一条消息,并想用伪元素和content添加它。 这里的陷阱是辅助技术的用户可能无法获得消息,这些消息在他们是否可以访问content方面是不一致的。 换句话说,为了确保可以访问“无结果”类型的消息,您需要将其添加为像段落这样的真实元素(隐藏的 div 将无法再访问aria-label )。

学习选择器的资源

CSS 有更多的选择器,包括伪类。 这里还有几个地方可以了解更多关于可用内容的信息:

  • MDN CSS 选择器文档包括一个全面的分类列表;
  • 我已经编写了一个由两部分组成的高级 CSS 选择器指南,您可以从第一部分开始;
  • 玩 CSS Diner 游戏,愉快地学习 CSS 选择器;
  • Kitty Giraudel 创建了一个选择器解释工具,它将分解和描述所提供选择器的各个部分。