Могу ли я объединить:nth-child () или:nth-of-type() с произвольным селектором?

#html #css

#css #css-селекторы

Вопрос:

Есть ли способ выбрать каждый 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() .

Кто-нибудь может объяснить почему?

Комментарии:

1. По состоянию на октябрь 2021 года указан nth-child (n из S) (где S — селектор), но, насколько я могу установить, в настоящее время реализован только в Safari, см., например, developer.mozilla.org/en-US/docs/Web/CSS/:nth-child

2. У Chromium и Firefox есть нерешенные проблемы с реализацией :nth-child(An B of S) .

Ответ №1:

Это очень распространенная проблема, которая возникает из-за непонимания того, как :nth-child(An B) и :nth-of-type() работают.

В селекторах уровня 3 :nth-child() псевдокласс подсчитывает элементы среди всех их братьев и сестер под одним и тем же родительским элементом. Не учитываются только родственные элементы, которые соответствуют остальной части селектора.

Аналогично, :nth-of-type() псевдокласс учитывает братьев и сестер, совместно использующих один и тот же тип элемента, который ссылается на имя тега в HTML, а не на остальную часть селектора.

Это также означает, что если все дочерние элементы одного и того же родителя имеют один и тот же тип элемента, например, в случае тела таблицы, единственными дочерними элементами которой являются tr элементы, или элемента списка, единственными дочерними элементами которого являются li элементы, то :nth-child() и :nth-of-type() будут вести себя одинаково, т.е. для каждого значения An B, :nth-child(An B) и :nth-of-type(An B) будут соответствовать одному и тому же набору элементов.

Фактически, все простые селекторы в данном составном селекторе, включая псевдоклассы, такие как :nth-child() и :not() , работают независимо друг от друга, вместо того, чтобы рассматривать подмножество элементов, которым соответствует остальная часть селектора.

Это также подразумевает, что нет понятия порядка среди простых селекторов в каждом отдельном составном селекторе1, что означает, например, что следующие два селектора эквивалентны:

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

В переводе на английский они оба означают:

Выберите любой tr элемент, который соответствует всем следующим независимым условиям:

  • это дочерний элемент с нечетным номером для своего родителя;
  • он имеет класс «row»; и
  • это потомок table элемента, который имеет класс «MyClass».

(вы заметите, что я использую здесь неупорядоченный список, просто для наглядности)

Уровень селекторов 4 направлен на устранение этого ограничения, позволяя :nth-child(An B of S) 2 принимать произвольный аргумент S селектора, опять же из-за того, что селекторы работают независимо друг от друга в составном селекторе, как продиктовано существующим синтаксисом селектора. Итак, в вашем случае это выглядело бы так:

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

Конечно, будучи совершенно новым предложением в совершенно новой спецификации, это, вероятно, не увидит реализации до нескольких лет в будущем.

Тем временем вам придется использовать скрипт для фильтрации элементов и соответствующего применения стилей или имен дополнительных классов. Например, ниже приведен обычный обходной путь с использованием jQuery (при условии, что в таблице есть только одна группа строк, заполненная tr элементами):

 $('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, или очищаете HTML с помощью таких инструментов, как BeautifulSoup, многие из этих инструментов допускают XPath в качестве альтернативы:

 //table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
  

Другие решения, использующие другие технологии, оставлены читателю в качестве упражнения; это всего лишь краткий, надуманный пример для иллюстрации.


1 Если вы указываете тип или универсальный селектор, он должен быть первым. Однако это принципиально не меняет принцип работы селекторов; это не более чем синтаксическая особенность.

2 Первоначально это было предложено как :nth-match() , однако, поскольку он по-прежнему учитывает элемент, относящийся только к его братьям и сестрам, а не к любому другому элементу, который соответствует данному селектору, с 2014 года он был переделан как расширение к существующему :nth-child() вместо этого.

Комментарии:

1. «(вы заметите, что я использую здесь неупорядоченный список, просто чтобы донести суть)» Я бы хотел, чтобы больше людей так же обдуманно относились к использованию упорядоченных и неупорядоченных маркеров. Спасибо за ваш ответ.

2. @The Red Pea: Одна из моих самых больших проблем с HTML: «В порядке от самого высокого / самого низкого к самому низкому / самому высокому:», за которым следует неупорядоченный список. Я имею в виду, да ладно, серьезно?

3. Это не недоразумение, а скорее запрос функции CSS, см. bram.us/2020/03/16/css-nth-of-class-selector

4. @Ingo Steinke: Вопрос не был сформулирован как запрос функции. Это было основано на непонимании того, как работает:nth-child().

Ответ №2:

Не совсем..

цитата из документов

:nth-child Псевдокласс соответствует элементу, который имеет b-1 родственных элементов перед ним в дереве документа, для заданного положительного или нулевого значения для n, и имеет родительский элемент.

Это собственный селектор, который не объединяется с классами. В вашем правиле это просто должно удовлетворять обоим селекторам одновременно, поэтому оно покажет :nth-child(even) строки таблицы, если они также имеют .row класс.

Ответ №3:

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 будет работать точно так же, как все элементы одного типа, то есть <div> поэтому 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-child и nth-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 элемента отличается от других .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 , а затем он применил css к 1-му, 2-му, 3-му, 4-му, 5-му элементу <p> .

Комментарии:

1. Насколько это полезно для <tr> тегов?

Ответ №4:

Возможно, вы сможете сделать это с помощью xpath. может сработать что-то вроде //tr[contains(@class, 'row') and position() mod 2 = 0] . Есть и другие вопросы SO, расширяющие детали того, как более точно сопоставлять классы.

Ответ №5:

Вот ваш ответ

 <!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>
  

Ответ №6:

Все вопросы, связанные с использованием 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;
}
  

Надеюсь, это поможет кому-то, кто идет по следу обмана в вопросах игнорирования скрытых элементов!

Комментарии:

1. Я бы хотел поддержать это, но для этого нужна рабочая демо-версия.

2. Насколько я помню, на самом деле это было не так надежно, как казалось изначально — теперь я бы выбрал комментарий с наибольшим количеством голосов, это невозможно.

Ответ №7:

ЕСЛИ у вас один и тот же родительский класс для всех селекторов, то вы используете этот класс, 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>
  

Ответ №8:

2023 ответ: Теперь вы можете!

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

Обобщенная версия (спецификация):

 :nth-child(<nth> [of <selector>]?) {}
  

…в котором <nth> это 2 , 3n 1 , -n 3 odd или другие допустимые значения и <selector> список селекторов, который может быть сложным.

На момент написания эта функция поддерживается только в Chrome 111 (2023/03/07, также для Android), Edge 111 (2023/03/13) и Safari 9 (2015/10/01, также для iOS), согласно caniuse.com .

Попробуйте:

 /*** 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>  

Ответ №9:

Не ответ на «Может ли кто-нибудь объяснить, почему?», поскольку другие ответы объяснили.

Но в качестве одного из возможных решений в вашей ситуации вы можете использовать пользовательские теги для строк и ячеек, скажем, <tr-row> , <td-row> тогда :nth-of-type() это должно сработать. Не забудьте задать стиль display: table-row; и display: table-cell; соответственно, чтобы они по-прежнему работали как ячейки таблицы.