Це дуже поширена проблема, яка виникає через нерозуміння того, як :nth-child()
і як :nth-of-type()
працювати. На жаль, наразі не існує рішення, засноване на селекторі, оскільки селектори не надають способу зіставити 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 рівня для конкретної мети вибору кожної п’ятої дитини, що відповідає заданому селектору. 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()
.