一些非常实用的 `:nth-child` 配方作为 Sass 混合宏

Avatar of Adam Giese
Adam Giese

DigitalOcean 提供适用于您旅程各个阶段的云产品。 立即开始使用 价值 $200 的免费积分!

不存在一劳永逸的样式。 三张图片的图片库可能需要与十二张图片的图片库采用不同的样式。 您可以在 CSS 中使用一些巧妙的技巧来添加一些基于数字的逻辑! 使用 :nth-child:nth-last-child,您可以在不离开样式表的情况下获得一些令人惊讶的复杂信息。

本文假设您基本了解 :nth-child 伪选择器的工作原理。 如果您需要快速复习,Chris 有一篇关于该主题的 精彩文章

编写复杂的 `:nth-child()` 选择器

您可能已经知道,除了 :nth-child 之外,还有一个相关的 :nth-last-child。 它与 :nth-child 的工作原理完全相同,只是它从父元素的 末尾 开始。 例如,您可以使用选择器 :nth-child(-n + 3) 来选择父元素的 前三个子元素。 您可以使用 :nth-last-child(-n + 3) 来选择 最后 三个子元素。 让我们来看一个无序列表作为示例。

查看 CodePen 上 Adam Giese 的 1. 选择最后三个

同样,您可以使用 :nth-last-child(n + 4) 来选择 最后三个以外的所有子元素。

查看 CodePen 上 Adam Giese 的 2. 选择除最后三个以外的所有子元素

当您开始将 :nth-last-child:first-child 结合使用时,真正的魔力就出现了。 通过使用这两个选择器来查看某个元素是否既是父元素的第一个子元素,又是最后三个元素之一,您可以仅在父元素最多包含三个元素时为第一个元素设置样式。

li:nth-last-child(-n + 3):first-child {
  /* your styling here */
}

继续我们的列表示例,以下是一种可视化此工作原理的方法

查看 CodePen 上 Adam Giese 的 3. 将 “first-child” 和 “last three” 链接在一起

相反,您可以使用 li:nth-last-child(n + 4):first-child 来选择具有四个或 更多 项的列表的第一个项

查看 CodePen 上 Adam Giese 的 4. 至少三个选择器

很酷吧? 但是很有可能,您想设置的样式不仅仅是列表中的第一个项。 通过使用普通兄弟选择器,您可以为紧随此第一个元素的任何列表项设置样式。 将这两个选择器用逗号隔开添加,您就可以选择所有元素。

li:nth-last-child(n + 4):first-child,
li:nth-last-child(n + 4):first-child ~ li {
  /* your styling here */
}

查看 CodePen 上 Adam Giese 的 5. 至少四个选择器

这带来了很多有趣的机会! 您也不必仅仅将自己限制在计算元素的总数。 通过将 :first-child:nth-last child 链接在一起,您可以检查任何有效的 :nth-child 表达式是否适用于元素的总数。 您可以编写样式,例如,如果子元素总数为奇数,如果子元素正好为七个,或者如果子元素为 3n + 2 个。

此技术的一个实际用例是为由 CMS 自动生成的下拉菜单设置样式。 例如,让我们来看一下由 WordPress wp_nav_menu() 函数生成的代码,为便于阅读已清理过

查看 CodePen 上 Adam Giese 的 WordPress 菜单 nth-child 示例

这看起来很标准。 将鼠标悬停在 Members 菜单项上,并注意子菜单项。

现在,让我们看看添加了更多成员的同一菜单

查看 CodePen 上 Adam Giese 的 WordPress 菜单 nth-child 示例,更多子元素

哇! 适用于四个项的样式无法很好地扩展到十二个项。 通过应用前面的示例,您可以纯粹在样式表中为 WordPress 默认菜单设置样式:无需编写自定义过滤器来更改根据菜单项数量而变化的类:您可以在样式表中保留样式。

查看 CodePen 上 Adam Giese 的 WordPress 菜单 nth-child 示例,紧凑版

以下是一些其他可能的用例。

局限性

您可以使用 :nth-child 做很多很酷的事情。 但是,也存在一些局限性。 例如,您无法为父元素设置样式,只能为子元素设置样式。 您无法根据 li 元素的数量为 ul 添加任何样式。

另一个需要考虑的问题是,是否使用 :nth-child:nth-of-type。 使用 :nth-child 将选择所有元素,这可能符合您的要求,也可能不符合您的要求。 例如,如果您想根据段落的数量为 .post-content 添加样式,则需要使用 p:nth-of-type。 不幸的是,使用这种方法,您只能为第一个段落标签之后(包括第一个段落标签)的子元素设置样式。 如果内容开头有一个 h2 标签,则您将无法根据 p 标签的数量为标题设置样式。 此外,nth-of-type 仅适用于元素:您将无法检查特定类的数量。

我发现,当应用于具有可预测兄弟元素的元素时,此技巧效果最佳。 li 是一个完美的候选元素,因为它们是 ulol 的唯一有效子元素。 自动生成的内容也可能效果很好。

抽象

这是一个很酷的技巧,但不幸的是语法有点繁琐。 但是,如果您使用 Sass,则可以轻松地添加一层抽象来使其更易于使用! 这将使使用这些复杂的选择器变得更易读、更直观。

由于所有这些选择器都基于一个模式,因此您可以从一个通用混合宏开始

@mixin has-nth($expression, $element: '*') {
  &:nth-last-child(#{$expression}):first-child,
  &:nth-last-child(#{$expression}):first-child ~ #{$element} {
    @content;
  }
}

将在 SCSS 中使用类似以下内容调用该混合宏

li {
  @include has-nth('n + 4', 'li') { //four or more
    /* your styling here */
  }
}

虽然这比编写纯 CSS 肯定要简洁得多,但您可以通过定义更多混合宏来添加另一层抽象。 以下是如何编写 at-least 混合宏的示例

@mixin at-least($quantity, $element: '*') {
  @include has-nth('n + #{$quantity}', $element) {
    @content;
  }
}

将在代码中像这样调用它们

li {
  @include at-least(4, 'li') { // four or more
    // styling goes here...
  }
}

可读性肯定有所提高! 您甚至可以通过嵌套混合宏来将它们链接在一起。

这里是使用一些复杂的 nth-child 逻辑的 Sass 混合宏的集合。

未来选择器

目前在 W3C 编辑器草案中有一些附加功能,这些功能可以极大地提高此技术的灵活性。 其中之一是 nth-child选择器列表 参数。 它的工作原理是在 nth-child 选择器中添加一个可选的附加参数。 它的工作原理类似于 nth-of-type 的工作原理,只是它适用于 任何 选择器,而不仅仅是元素。 例如,您可以使用选择器 li:nth-child(odd of .active) 为带有类 active 的所有其他列表项设置样式。 使用此功能,我们可以检测特定类的数量:例如

li.active:nth-last-child(n + 5 of .active).first-child,
li:nth-last-child(n + 5 of .active).first-child ~ li.active {
  /* styling goes here... */
}

如果至少有五个活动列表项,这将为它们设置样式。 您可以在 此处了解有关选择器列表的更多信息。 然而,目前浏览器支持非常有限; 它仅在 Safari(iOS 和 OSX)上可用。

另一个功能是 关系伪类选择器。 此建议的选择器接受一个 选择器列表 参数,并在选择器相对于基本选择器存在时匹配。 例如,您可以为 ul:has(li) 设置样式,以对至少包含一个 li 的任何列表添加样式。 您还可以添加更复杂的选择器:.post-content:has(h1, h2, h3, h4, h5, h6) 可以为至少包含一个标题元素的任何 .post-content 设置样式。 将此功能与我们学到的某些高级 nth-child 配方结合使用,我们可以编写 ul:has(li:nth-last-child(n + 5):first-child) 来为至少包含五个 li 的任何 ul 设置样式。

截至目前,按数量设置样式主要适用于为具有可预测兄弟元素的元素设置样式,例如列表或表格。 如果这两个功能得到支持,那么此技术将更加灵活。