Это очень частая проблема, возникающая из-за непонимания того, как :nth-child()
и :nth-of-type()
работают. К сожалению, в настоящее время нет решения на основе селектора, потому что Selectors не предоставляет способ сопоставления n-го дочернего элемента, который соответствует произвольному селектору на основе шаблона, такого как нечетный, четный или любой другой, an+b
где a != 1
и b != 0
. Это распространяется не только на селекторы классов, но и на селекторы атрибутов, отрицания и более сложные комбинации простых селекторов.
: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
элемент, который соответствует всем следующим независимым условиям:
- это нечетный дочерний элемент своего родителя;
- у него есть класс "строка"; и
- это потомок
table
элемента с классом myClass.
(вы заметите, что я использую здесь неупорядоченный список, просто чтобы довести дело до конца)
Поскольку в настоящее время нет решения на чистом CSS, вам придется использовать сценарий для фильтрации элементов и соответственно применения стилей или дополнительных имен классов. Например, следующее является обычным обходным путем с использованием 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 с помощью таких инструментов, как lxml, многие из этих инструментов позволяют использовать XPath в качестве альтернативы:
//table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
Другие решения, использующие другие технологии, оставлены читателю в качестве упражнения; это всего лишь краткий надуманный пример для иллюстрации.
Как бы то ни было, есть предложение о расширении :nth-child()
нотации, которое будет добавлено к уровню 4 селекторов для конкретной цели выбора каждого n-го дочернего элемента, соответствующего данному селектору. 2
Селектор, с помощью которого фильтруются совпадения, предоставляется в качестве аргумента :nth-child()
, опять же из-за того, как селекторы работают независимо друг от друга в последовательности, продиктованной существующим синтаксисом селектора. Итак, в вашем случае это будет выглядеть так:
table.myClass tr:nth-child(odd of .row)
(Читатель проницательный заметит сразу , что это должно быть :nth-child(odd of tr.row)
вместо этого, так как простых селекторов tr
и :nth-child()
работают независимо друг от друга , а также. Это одна из проблем , связанных с функциональными псевдо-классов , которые принимают селекторы, банкой червей я лучше не открывать в середине этого ответа. Вместо этого я буду исходить из предположения, что на большинстве сайтов не будет никаких других элементов, кроме tr
элементов, являющихся родственниками друг друга в теле таблицы, что сделало бы любой вариант функционально эквивалентным.)
Конечно, поскольку это совершенно новое предложение в совершенно новой спецификации, оно, вероятно, не будет реализовано раньше, чем через несколько лет. А пока вам придется использовать скрипт, как указано выше.
1 Если вы указываете тип или универсальный селектор, он должен быть первым. Однако это не меняет фундаментальных принципов работы селекторов; это не более чем синтаксическая причуда.
2 Первоначально это было предложено, поскольку :nth-match()
, однако, поскольку он по-прежнему считает элемент относительно только его братьев и сестер, а не всех других элементов, соответствующих данному селектору, с 2014 года он был перепрофилирован как расширение существующего :nth-child()
.