Псевдокласс :nth-child() появился ещё в третьей версии спецификации селекторов, а его актуальная грамматика описана в Selectors Level 4. Он выбирает элементы по порядковому номеру в списке детей родителя: третий пункт меню, каждая чётная строка таблицы, каждый третий товар в сетке. Принципиальный момент – позиция считается среди всех соседних элементов независимо от тега. Этим он отличается от :nth-of-type(), который нумерует только элементы одного типа, и этим же объясняется большинство ситуаций «селектор не работает» у новичков.
Как это работает
Браузер перебирает детей родителя и сверяет номер каждого с паттерном из аргумента. Нумерация начинается с 1, а не с 0: :nth-child(1) эквивалентен :first-child, а :nth-child(0) не выберет ничего. Аргумент принимает три формы:
- Число – конкретная позиция:
li:nth-child(3)выберет третий элемент списка. - Ключевые слова –
odd(нечётные позиции: 1, 3, 5…) иeven(чётные: 2, 4, 6…), полные синонимы формул2n+1и2n. - Формула An+B – A задаёт шаг, B – смещение, а n пробегает неотрицательные целые числа: 0, 1, 2 и далее. Если результат формулы нулевой или отрицательный, элемент на такой позиции просто не выбирается.
Проверить любую формулу можно простой подстановкой: для 3n+4 при n = 0, 1, 2 получаются позиции 4, 7 и 10 – каждый третий элемент начиная с четвёртого. Отрицательный коэффициент разворачивает выборку: -n+3 даёт позиции 3, 2 и 1, то есть первые три элемента.
/* зебра списка */
li:nth-child(even) { background-color: lightyellow; }
/* первые три пункта */
li:nth-child(-n + 3) { border: 2px solid orange; }
/* вторая колонка любой таблицы */
tbody tr :nth-child(2) { text-align: center; }Формула An+B: таблица-шпаргалка
| Запись | Какие позиции | Типовая задача |
|---|---|---|
2n (even) | 2, 4, 6… | зебра списков и таблиц |
2n+1 (odd) | 1, 3, 5… | зебра с первой строки |
3n | 3, 6, 9… | каждый третий элемент сетки |
3n+4 | 4, 7, 10… | каждый третий начиная с четвёртого |
n+7 | с 7-й до конца | весь «хвост» длинного списка |
-n+3 | первые три | акцент на первых карточках |
1 | только первая | эквивалент :first-child |
Диапазон задаётся цепочкой из двух условий: :nth-child(n+8):nth-child(-n+15) выберет элементы с 8-го по 15-й включительно. Отсчёт с конца – задача зеркального псевдокласса :nth-last-child(): запись :nth-last-child(-n+3) означает «последние три».
Почему li:nth-child(2) может «не работать»
В селекторе span:nth-child(2n+1) позиция считается среди всех соседей любого типа, но правило применяется только к тем нечётным позициям, где действительно стоит span. Классический пример из документации MDN: у родителя пять детей – span, span, span, em, span. Выделятся span на позициях 1, 3 и 5, а em на позиции 4 визуально «ломает» ожидаемое чередование: подсвеченные третий и пятый span оказываются рядом. Если считать нужно только элементы конкретного тега – берите :nth-of-type(), а если фильтровать по произвольному селектору – синтаксис of S, где браузер сначала отбирает детей по селектору и лишь потом нумерует.
Зачем это нужно
Главная ценность – стилизация по позиции без лишних классов в разметке и без JavaScript. Типовые задачи:
- Зебра – чередование фона строк таблицы или пунктов списка, самый цитируемый кейс во всех справочниках.
- Ритмичная сетка – каждый третий или четвёртый элемент каталога получает другой размер или отступ.
- Акценты на краях – первые N карточек крупнее, у последних нет разделителя.
- Вёрстка, зависящая от количества – комбинации :nth-child() и :nth-last-child() меняют стиль в зависимости от числа элементов в контейнере.
Есть и обратная сторона: :nth-child() привязывает оформление к порядку в разметке. Если акцент должен зависеть от данных, а не от позиции – например, выделять товары со скидкой, где бы они ни стояли, – честнее использовать классы или data-атрибуты, а позиционные псевдоклассы оставить для ритма и чередования.
Для бизнеса это означает меньше правок шаблонов: контент-менеджер добавляет и удаляет элементы, а чередование и акценты перестраиваются сами. С поддержкой всё спокойно: базовый :nth-child() работает во всех браузерах с июля 2015 года (статус Baseline Widely Available по MDN), а исторически поддерживался ещё с IE9. По специфичности это обычный псевдокласс – (0,1,0), столько же, сколько у класса.
Пример
Каталог товаров: нужно выделить первые три карточки как хиты, задать зебру спискам характеристик и подсветить строки прайса с 4-й по 8-ю, где стоят акционные позиции.
/* первые три карточки – хиты */
.catalog .card:nth-child(-n + 3) { box-shadow: 0 0 0 2px coral; }
/* зебра характеристик */
.specs li:nth-child(even) { background-color: lightyellow; }
/* акционные строки прайса: с 4-й по 8-ю */
.price tr:nth-child(n + 4):nth-child(-n + 8) { background-color: honeydew; }На Битрикс-проектах я использую этот приём постоянно: компоненты вида news.list выводят элементы однородным списком, и позиционные псевдоклассы позволяют оформить «каждый третий баннер шире» или «первые два поста крупнее» без правки PHP-логики и без условных классов в шаблоне. Единственная ловушка – убедиться, что в контейнере нет посторонних детей вроде технических обёрток или скрытых блоков: они тоже занимают позиции и сдвигают нумерацию. В шаблонах Битрикса такое случается регулярно: отложенный баннер, пустой div под скрипт или невидимый элемент пагинации становятся «лишними» детьми контейнера и смещают зебру на одну позицию. Если скрытые элементы неизбежны, зебру спасает пре-фильтр of :not([hidden]) – подробный разбор в статье о синтаксисе of S.