Отдельного селектора «:nth-child-of» в CSS не существует – of S это необязательная часть аргумента псевдоклассов :nth-child() и :nth-last-child(), описанная в Selectors Level 4. Запись :nth-child(2 of .highlight) означает «второй элемент среди детей, подходящих под селектор .highlight». Разработчики годами просили гипотетический «:nth-of-class» – возможность считать позиции не по тегу, как делает :nth-of-type(), а по произвольному селектору. Синтаксис of S закрыл именно этот запрос.
Как это работает
Грамматика по спецификации: :nth-child( [ An+B | even | odd ] of <complex-selector-list> ). Браузер действует в два шага: сначала отбирает среди детей родителя тех, кто матчит селектор S, затем применяет формулу An+B только внутри отфильтрованного набора. Остальные дети полностью игнорируются – для нумерации их как будто не существует. Элемент попадает под правило, только если он и матчит S, и стоит на нужной позиции внутри этого подмножества. Вместо формулы допустимы и ключевые слова: :nth-child(odd of .sale) возьмёт нечётные по счёту элементы среди детей с классом .sale.
S – полноценный список селекторов, а не только одиночный класс:
- Несколько селекторов через запятую –
:nth-child(4 of .highlight, .sale)выберет четвёртый элемент среди детей с классом .highlight или .sale. - Псевдоклассы внутри S –
:nth-child(even of :not([hidden]))считает только видимые элементы; сам механизм отрицания разобран в статье о :not().
Важное ограничение: of S есть только у :nth-child() и :nth-last-child(). У :nth-of-type() такого аргумента нет – и он не нужен, потому что of S с селектором тега полностью перекрывает его возможности.
Пре-фильтр и пост-фильтр – в чём разница
Главный источник путаницы – отличие of S от привычной комбинации класса с псевдоклассом. Записи похожи, результат принципиально разный:
| Запись | Как считает браузер | Итог |
|---|---|---|
.cls:nth-child(2) | берёт второго ребёнка среди всех детей и проверяет, есть ли у него класс | пост-фильтр: если второй ребёнок без .cls – не выберется ничего |
:nth-child(2 of .cls) | сначала отбирает детей с .cls, затем берёт второго из них | пре-фильтр: второй элемент с классом, где бы он ни стоял |
li.noted:nth-child(-n+3) | ищет .noted среди первых трёх детей вообще | если первые дети без класса – результат пустой |
li:nth-child(-n+3 of .noted) | нумерует только .noted и берёт первые три | первые три .noted при любых абсолютных позициях |
/* второй элемент среди .highlight */
:nth-child(2 of .highlight) { outline: 2px solid coral; }
/* первые три .noted, где бы они ни стояли */
li:nth-child(-n + 3 of .noted) { border: 2px solid orange; }
/* сравните: .noted только среди первых трёх детей вообще */
li.noted:nth-child(-n + 3) { /* другой результат */ }Зачем это нужно
Канонический кейс, который приводят и MDN, и Chrome for Developers, – зебра таблицы, не ломающаяся на скрытых строках. Обычный tr:nth-child(even) считает все строки, включая спрятанные фильтром: скройте одну – и чередование собьётся, рядом окажутся две строки одного цвета. Пре-фильтр решает проблему одной строкой кода: tr:nth-child(even of :not([hidden])) нумерует только видимые строки, и зебра остаётся ровной при любом состоянии фильтра.
Второй выигрыш – экономия на JavaScript. До кросс-браузерной поддержки такие задачи решали пересчётом классов скриптом либо полифиллом: известен паттерн на комбинации :has() и :not(), опубликованный Брамусом ван Даммом в декабре 2022 года. Теперь достаточно чистого CSS. Учитывайте только специфичность: по Selectors Level 4 к весу самого псевдокласса добавляется специфичность самого специфичного селектора из списка S.
Третий сценарий – разнотипные списки. В ленте, где вперемешку идут карточки статей, рекламные блоки и разделители, of S позволяет стилизовать «каждую вторую статью», не трогая остальных соседей: :nth-child(even of .post). Раньше для этого пришлось бы либо чистить разметку, либо навешивать классы скриптом при каждом изменении списка.
| Браузер | Минимальная версия |
|---|---|
| Safari | 9+ (2015 год, реализовал первым) |
| Chrome и Edge | 111+ (март 2023) |
| Firefox | 113+ (май 2023) |
| Opera | 98+ |
| Samsung Internet | 22+ |
| IE и Opera Mini | нет поддержки |
С мая 2023 года, когда фичу получил Firefox, синтаксис имеет статус Baseline 2023 (Newly available). Глобальная поддержка по caniuse на июль 2026 – около 92%.
Пример
Типовая задача на Битрикс-проектах – таблица заказов или товаров с живой фильтрацией: JavaScript скрывает неподходящие строки атрибутом hidden, не перестраивая DOM. Обычная зебра в такой таблице разваливается после первого же клика по фильтру.
/* зебра только по видимым строкам */
.orders tr:nth-child(even of :not([hidden])) {
background-color: lightgrey;
}
/* последние три строки с классом .important */
.orders tr:nth-last-child(-n + 3 of .important) {
font-weight: bold;
}Первое правило красит каждую вторую видимую строку: скрытые не участвуют в нумерации, и чередование остаётся ровным при любом наборе фильтров. Второе выделяет последние три строки с классом .important – отсчёт с конца делает :nth-last-child() с тем же пре-фильтром. Перед выкаткой загляните в отчёт Яндекс Метрики по браузерам вашей аудитории: при глобальной поддержке около 92% фолбэк чаще всего не нужен, но если заметна доля старых Android-браузеров или Opera Mini, оставьте обычную зебру запасным вариантом. Тот же приём работает в сетке товаров каталога: скрывайте отфильтрованные позиции атрибутом hidden – и чередование карточек останется ровным без единой строки JavaScript. Как устроены сами формулы An+B, odd/even и диапазоны – в базовой статье о псевдоклассе :nth-child().