选择器内部的复杂 CSS 选择器

自去年以来,WebKit 支持 CSS Selectors Level 4 草案中定义的许多选择器。

在这篇文章中,我们将看到一个特殊的新选择器子集:将其他选择器作为参数的新伪类。它们是 :matches():not():nth-child():nth-last-child()

本文讨论的所有选择器都适用于 CSS 样式设置以及 JavaScript 查询(querySelector()querySelectorAll()closest() 等)。

选择属性 A 或 B

伪类 :matches() 接受一个选择器列表作为参数。如果列表中的任何选择器匹配,它就会匹配一个元素。

例如,如果我们有

:matches(.foo, .bar) {
    background-color: green;
}

任何带有“foo”、“bar”类或两者兼有的元素都将具有绿色背景。

这个新选择器在 WebKit 内部被广泛使用,以简化样式表。例如,以前 Web Inspector 中会出现类似以下的代码

.syntax-highlighted .css-keyword,
.syntax-highlighted .css-tag {
    color: rgb(170, 13, 145);
}

使用 :matches(),作用域选择器不再需要重复

.syntax-highlighted :matches(.css-keyword, .css-tag) {
    color: rgb(170, 13, 145);
}

完整的选择器支持

:matches() 内部的参数不必是简单选择器的列表。任何选择器都受支持,包括使用组合器。

例如,以下规则会在两个标题级别彼此相邻时移除顶部外边距

:matches(h1+h2, h2+h3, h3+h4, h4+h5) {
    margin-top: 0;
}

当将组合器与 :matches() 一起使用时,对于选择器的主题存在一些混淆。例如,看以下规则

.icon:matches(:any-link img) {}

它匹配每个带有“icon”类且其祖先中包含链接的图像。它匹配包含图像元素的链接。实际上,它严格等同于

:any-link img.icon { }

如果您不确定 :matches() 将应用于哪个元素,您可以简单地将其视为选择器正在对活动元素调用 Element.matches()

废弃的 :-webkit-any

WebKit 已经有一个类似但更有限的功能,即伪类 :-webkit-any()。它现在已被废弃。

旧的 :-webkit-any() 存在无法以向后兼容方式修复的问题。最好避免使用它。我们已将其在 WebKit 内部的所有使用都移除了。

在用 :matches() 替换 :-webkit-any() 时,请务必仔细测试您的页面,因为它们并非严格等效。特别是,:-webkit-any() 无法正确处理特异性。

拒绝完整选择器

伪类 :not() 在 CSS 中一直非常受欢迎。如果元素不匹配简单选择器,它就会选择该元素。例如

a:not(.internal) {
    color: red;
}

新颖之处在于您可以传递给 :not() 的参数。就像 :matches() 一样,新的 :not() 支持任何选择器列表作为参数。

以前需要链接多个 :not() 的代码现在可以使用逗号分隔的选择器来实现相同的功能。例如

:not(i):not(em) { }

也可以写成

:not(i, em) { }

您可以使用复杂选择器,这意味着您可以排除多个属性。例如,现在可以选择任何不同时具有“.important”和“.dialog”类的元素

:not(.important.dialog) { }

您可以使用组合器,这正在成为我最喜欢的功能之一。例如,可以选择任何不属于链接的图像

img:not(:any-link *) { }

计数元素

伪类 :nth-child():nth-last-child() 也得到了类似功能的增强。

以前,只能用 :nth-child() 不加区分地计数元素,或者用 :nth-of-type() 计数具有相同限定名称的元素。通过扩展的 :nth-child(),您可以指定要计数的元素类型。

例如,如果您想选择每隔一个未隐藏的元素,您现在可以这样做

:nth-child(odd of :not([hidden])) { }

通用语法形式为

:nth-child(An+B of selector list) { }

至于以前的选择器,参数完全支持选择器,包括复杂选择器、组合器等。

动态特异性

在其他选择器内部嵌套选择器产生了一个新的有趣问题:结果的特异性是什么?

答案是:这取决于。特异性随所匹配的内容而变化。最终特异性是所有子选择器中最大的。

让我们举一个具体的例子。如果我们采用以下选择器

:matches(div, #foo, .bar) { }

特异性是多少?

当选择以下元素时

<div id="foo"></div>

特异性是 (1, 0, 0),与我们只有选择器“#foo”时的特异性相同。原因是对于所有匹配的选择器(“div”和“#foo”),两者中最高的特异性是“#foo”的特异性。

这个定义很棒,因为它与现有选择器兼容。您可以取任何选择器,将其包装在 :matches() 内部,其特异性表现完全相同。

对于复杂选择器,查找特异性并非总是那么容易。幸运的是,Web Inspector 可以帮助我们。

在 Inspector 中,当鼠标悬停在“规则样式”侧边栏中的选择器上时,工具提示会显示当前元素选择器的真实特异性。如果特异性随元素而异(如上例所示),Inspector 还会指示特异性是动态的

The Web Inspector's tooltip show the dynamic specificity of any selector.

结语

新选择器带来了许多新的可能性。尝试它们,发明新的组合,并告诉我们您的想法。

如需简短问题,您可以通过 Twitter 联系Yusuke。如需较长问题,您可以发送电子邮件至webkit-help提交 bug 报告