我能将:nth-child()或:nth-of-type()与任意选择器结合使用吗?

148

有没有一种方法可以选择每个满足(或不满足)任意选择器的第n个子元素?例如,我想选择每个奇数表格行,但是仅在一部分行中:

table.myClass tr.row:nth-child(odd) {
  ...
}
<table class="myClass">
  <tr>
    <td>Row
  <tr class="row"> <!-- I want this -->
    <td>Row
  <tr class="row">
    <td>Row
  <tr class="row"> <!-- And this -->
    <td>Row
</table>

然而 :nth-child() 看起来只是计算所有的 tr 元素,无论它们是否属于 "row" 类,因此我最终得到了一个偶数 "row" 元素,而不是我要找的两个。使用 :nth-of-type() 同样会出现这种情况。

有人能解释一下为什么吗?


3
截至2021年10月,nth-child(n of S)已被规定(其中S为选择器),但据我所知目前仅在Safari中实现。例如,请参见https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child。 - A Haworth
Chromium和Firefox都有开放问题来实现:nth-child(An+B of S) - mfluehr
9个回答

173

新语法的支持现在非常好,所以你可以使用:

table.myClass tr:nth-child(odd of .row)

这是一个非常常见的问题,它是由于对:nth-child(An+B):nth-of-type()的工作原理的误解而引起的。
在选择器 Level 3 中,:nth-child() 伪类计算的是在同一个父元素下的所有兄弟元素中的位置。它不仅仅计算与选择器的其余部分匹配的兄弟元素。
同样地,:nth-of-type() 伪类计算的是具有相同元素类型的兄弟元素,这指的是 HTML 中的标签名,而不是选择器的其余部分。
这也意味着,如果同一个父元素的所有子元素都是相同的元素类型,例如表格主体只有元素或列表元素只有
  • 元素,那么:nth-child()和:nth-of-type()的行为将完全相同,即对于An+B的每个值,:nth-child(An+B)和:nth-of-type(An+B)将匹配相同的一组元素。
    实际上,在给定的复合选择器中,包括:nth-child()和:not()等伪类在内的所有简单选择器都是相互独立工作的,而不是查看其余选择器匹配的元素子集。
    这也意味着,在每个单独的复合选择器中,简单选择器之间没有顺序的概念,这意味着例如以下两个选择器是等效的:
    table.myClass tr.row:nth-child(odd)
    table.myClass tr:nth-child(odd).row
    

    翻译成中文,它们的意思都是:

    选择满足以下独立条件的任何tr元素:

    • 它是其父元素的奇数个子元素;
    • 它具有"class"为"row"的类;
    • 它是具有"class"为"myClass"的table元素的后代。

    (你会注意到我在这里使用了无序列表,只是为了强调这一点)

    选择器级别4试图通过允许:nth-child(An+B of S)2接受任意选择器参数S来纠正这个限制,这是由于选择器在复合选择器中独立操作的方式所决定的,这是现有选择器语法所规定的。所以在你的情况下,它会是这样的:

    table.myClass tr:nth-child(odd of .row)
    

    你还可以使用脚本来过滤元素并根据需要应用样式或额外的类名。例如,以下是使用jQuery的常见解决方法(假设表格中只有一个包含元素的行组):
    $('table.myClass').each(function() {
      // Note that, confusingly, jQuery's filter pseudos are 0-indexed
      // while CSS :nth-child() is 1-indexed
      $('tr.row:even').addClass('odd');
    });
    

    使用相应的CSS:
    table.myClass tr.row.odd {
      ...
    }
    

    如果您正在使用自动化测试工具,如Selenium,或者使用类似BeautifulSoup的工具来抓取HTML,许多这些工具都允许使用XPath作为替代方法。
    //table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
    

    其他使用不同技术的解决方案留给读者自行探索;这只是一个简短的、刻意构造的例子,用于说明。

    1 如果您指定了类型或通用选择器,它必须放在最前面。然而,这并不会改变选择器的基本工作原理;它只是一个语法上的怪癖。

    2 最初提出的是:nth-match(),但由于它仅相对于其兄弟元素计算,而不是与匹配给定选择器的每个其他元素计算,因此自2014年以来,它已被重新用作现有:nth-child()的扩展。


  • 5
    "(你会注意到我在这里使用了一个无序列表,只是为了更好地阐述重点)我希望更多的人在使用有序和无序标记时能够谨慎选择。感谢您的答复。" - Nate Anderson
    3
    我很不喜欢HTML中的一个问题:“按从高到低/从低到高”的排序,然后接着是无序列表。我是说,这真的合理吗? - BoltClock
    这不是误解,而是一个CSS功能请求,请参见https://www.bram.us/2020/03/16/css-nth-of-class-selector/。 - Ingo Steinke
    @Ingo Steinke:这个问题并不是作为一个功能请求来表述的。它基于对:nth-child()如何工作的误解。 - BoltClock

    8

    不完全是这样。

    文档中的引用

    :nth-child 伪类匹配在文档树中它前面有 an+b-1 个兄弟元素的元素,对于给定的正数或零值 n,并且有一个父元素。

    它是一种独立的选择器,不与类结合使用。在您的规则中,它只需同时满足两个选择器,因此如果 :nth-child(even) 表格行也恰好具有 .row 类,则会显示它们。


    2

    nth-of-type按照相同类型元素的索引工作,而nth-child仅根据索引工作,不考虑兄弟元素的类型。

    例如

    <div class="one">...</div>
    <div class="two">...</div>
    <div class="three">...</div>
    <div class="four">...</div>
    <div class="five">...</div>
    <div class="rest">...</div>
    <div class="rest">...</div>
    <div class="rest">...</div>
    <div class="rest">...</div>
    <div class="rest">...</div>
    

    假设在上面的HTML中,我们想要隐藏所有具有rest类的元素。
    在这种情况下,nth-child和nth-of-type将完全相同,因为所有元素都是相同类型的
    ,因此CSS应该是:
    .rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
        display:none;
    }
    

    或者

    .rest:nth-of-type(6), .rest:nth-of-type(7), .rest:nth-of-type(8), .rest:nth-of-type(9), .rest:nth-of-type(10){
        display:none;
    }
    

    现在你一定想知道nth-childnth-of-type之间有什么区别,那么这就是它们的区别:

    假设html如下:

    <div class="one">...</div>
    <div class="two">...</div>
    <div class="three">...</div>
    <div class="four">...</div>
    <div class="five">...</div>
    <p class="rest">...</p>
    <p class="rest">...</p>
    <p class="rest">...</p>
    <p class="rest">...</p>
    <p class="rest">...</p>
    

    在上面的HTML中,.rest元素的类型与其他元素不同,其他元素是div,而这些元素是段落。因此,在这种情况下,如果您使用nth-child,您需要写成这样:
    .rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
        display:none;
    }
    

    但是如果你使用nth-of-type选择器,CSS代码可以变成这样。
    .rest:nth-of-type(1), .rest:nth-of-type(2), .rest:nth-of-type(3), .rest:nth-of-type(4), .rest:nth-of-type(5){
        display:none;
    }
    

    由于.rest元素的类型是<p>,因此这里的nth-of-type检测到了.rest的类型,然后在<p>的第1、2、3、4、5个元素上应用了CSS。

    对于 <tr> 标签来说,这有多有用? - Alexis Wilke

    1
    这是你的答案。
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>TEST</title>
    
      <style>
    
        .block {
          background: #fc0;
          margin-bottom: 10px;
          padding: 10px;
        }
        /* .large > .large-item:nth-of-type(n+5) {
          background: #f00;
        } */
    
        .large-item ~ .large-item ~ .large-item ~ .large-item ~ .large-item {
          background: #f00;
        }
    
      </style>
    </head>
    <body>
    
    <h1>Should be the 6th Hello Block that start red</h1>
    <div class="small large">
      <div class="block small-item">Hello block 1</div>
      <div class="block small-item large-item">Hello block 2</div>
      <div class="block small-item large-item">Hello block 3</div>
      <div class="block small-item large-item">Hello block 4</div>
      <div class="block small-item large-item">Hello block 5</div>
      <div class="block small-item large-item">Hello block 6</div>
      <div class="block small-item large-item">Hello block 7</div>
      <div class="block small-item large-item">Hello block 8</div>
    </div>
    
    </body>
    </html>
    

    1
    你可以使用xpath来实现这个目的。类似于//tr[contains(@class, 'row') and position() mod 2 = 0]这样的代码可能会起作用。还有其他的SO问题详细介绍了如何更精确地匹配类。

    1
    2023的答案:现在你可以了!
    table.myClass tr:nth-child(odd of .row) {}
    

    通用版本(spec):
    :nth-child(<nth> [of <selector>]?) {}
    

    ...其中<nth>可以是23n + 1-n + 3奇数或者其他有效值,而<selector>是一个可能很复杂的选择器列表。

    截至目前,根据caniuse.com的数据,这个功能只在Chrome 111+(2023/03/07,包括Android)、Firefox 113+(2023/05/09,包括Android)、Edge 111+(2023/03/13)和Safari 9+(2015/10/01,包括iOS)上得到支持。点击此处查看详情

    试一试:

    /*** First example ***/
    
    /* All odds (2n + 1) */
    #example-1 > :nth-child(odd of .foo) {
      background: red;
    }
    
    /* Odd evens (4n + 2) */
    /* Note how .foo:not(...) is evaluated first */
    #example-1 > :nth-child(odd of .foo:not(:nth-child(odd of .foo))) {
      background: yellow;
    }
    
    /*** Second example ***/
    
    /* This works */
    #example-2 > :nth-of-type(3) {
      background: red;
    }
    
    /* This too */
    #example-2 > :nth-child(4 of div) {
      background: yellow;
    }
    
    /* And so will this */
    #example-2 > :nth-last-child(11 of div) {
      background: green;
    }
    
    /* Use :nth-last-child() to select from last */
    /* First 2 of last 3 of <div>s */
    #example-2 > :nth-child(-n + 2 of :nth-last-child(-n + 3 of div)) {
      background: cyan;
    }
    
    /* 1st, 4th, 7th... from last */
    #example-2 > :nth-last-child(3n + 1 of div) {
      text-decoration: underline;
    }
    
    /* ...of which odd elements will be selected */
    #example-2 > :nth-last-child(odd of :nth-last-child(3n + 1)) {
      outline: 3px solid black;
    }
    
    /*** Other styles ***/
    
    .flex {
      display: flex;
      flex-flow: row wrap;
    }
    
    .grid {
      display: grid;
      grid-template: repeat(5, 1fr) / repeat(5, 1fr);
    }
    
    .flex > div,
    .grid > div {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100px;
      width: 100px;
      text-align: center;
    }
    
    /* 2nd, 3rd, 4th... of both .flex and .grid */
    body > :nth-child(n + 2 of .flex, .grid) {
      gap: 10px;
    }
    
    /* First 1 of [2nd, 3rd, 4th...] <div>s */
    body > :nth-child(-n + 1 of :nth-child(n + 2 of div)) > div {
      background: #ddd;
    }
    
    /* 2nd of last 2 of [1st, 2nd, 3rd...] non-<script> from last */
    /* ...which means 1st from last */
    :nth-child(odd of :nth-last-child(-n + 3 of body > :not(script))) > .foo {
      border-radius: 50%;
    }
    :nth-child(odd of :nth-last-child(-n + 3 of body > :not(script))) > .foo::after {
      content: '.foo';
      display: block;
    }
    <div class="flex" id="example-1">
      <div>odd</div>
      <div>even</div>
      <div class="foo">1st odd</div>
      <div class="foo">1st even</div>
      <div class="foo">2nd odd</div>
      <div class="foo">2nd even</div>
      <div class="foo">3rd odd</div>
      <div class="foo">3rd even</div>
    </div>
    
    <hr>
    
    <div class="flex" id="example-2">
      <div>1 (15)</div>
      <div class="foo">2 (14)</div>
      <div>3 (13)</div>
      <div class="foo">4 (12)</div>
      <div>5 (11)</div>
      <div>6 (10)</div>
      <div class="foo">7 (9)</div>
      <div>8 (8)</div>
      <div>9 (7)</div>
      <div class="foo">10 (6)</div>
      <div class="foo">11 (5)</div>
      <div>12 (4)</div>
      <div class="foo">13 (3)</div>
      <div>14 (2)</div>
      <div>15 (1)</div>
    </div>


    1
    所有关于使用nth-child和跳过隐藏标签的问题似乎都被重定向为此问题的副本,因此我将在此留下这个。我发现这篇博客https://blog.blackbam.at/2015/04/09/css-nth-child-selector-ignore-hidden-element/使用了巧妙的CSS方法来使nth-child忽略隐藏元素,如下所示:
    以下CSS会向每个第二个可见元素添加右边距,无论哪个元素具有cpw类。
    .cpw {
        display:none;
    }
    
    .video_prewrap {
        margin-right:20px;
    }
    
    .video_prewrap:nth-child(2n) {
        margin-right:0;
    }
    
    .cpw ~ .video_prewrap:nth-child(2n) {
        margin-right:20px;
    }
    
    .cpw ~ .video_prewrap:nth-child(2n-1) {
        margin-right:0;
    }
    

    希望这能对那些正在追踪忽略隐藏元素问题的人有所帮助!

    3
    我想点赞,但需要一个可用的演示。 - Moob
    1
    我的记忆是,这实际上并不像最初看起来那么强大 - 我现在会选择得票最高的评论,即这是不可能的。 - IrishDubGuy

    1
    如果您有相同的父类用于所有选择器,则可以使用该类:document.querySelector("main .box-value:nth-child(3) select.priorityOption");。因为在这种情况下,document.querySelector("main .box-value select.priorityOption:nth-child(3)");不能工作。谢谢。
    <div class="card table">
        <div class="box">
            <div class="box-value">
                <select class="priorityOption">
                    <option value="">--</option>
                    <option value="">LOREM</option>
                    <option value="">LOREM</option>
                </select>
            </div>
    
            <div class="box-value">
                <select class="priorityOption">
                    <option value="">--</option>
                    <option value="">LOREM</option>
                    <option value="">LOREM</option>
                </select>
            </div>
    
            <div class="box-value">
                <select class="priorityOption">
                    <option value="">--</option>
                    <option value="">LOREM</option>
                    <option value="">LOREM</option>
                </select>
            </div>
        </div>
    </div>
    

    -1

    这不是回答“有人能解释为什么吗?”的问题,因为其他答案已经解释了。

    但作为您情况下的一种可能的解决方案,您可以使用自定义标签来表示行和单元格,例如<tr-row><td-row>,然后:nth-of-type()应该可以工作。别忘了分别设置样式display: table-row;display: table-cell;以使它们仍像表格单元格一样工作。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接