Синтаксис of S

Синтаксис of S – часть аргумента :nth-child() и :nth-last-child() из Selectors Level 4: браузер сначала отбирает детей по селектору S, а затем считает позиции только среди них. Во всех браузерах – с 2023 года.

4 минуты чтения

Отдельного селектора «: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 от привычной комбинации класса с псевдоклассом. Записи похожи, результат принципиально разный:

Пре-фильтр 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). Раньше для этого пришлось бы либо чистить разметку, либо навешивать классы скриптом при каждом изменении списка.

Поддержка синтаксиса of S в браузерах (caniuse, июль 2026)
БраузерМинимальная версия
Safari9+ (2015 год, реализовал первым)
Chrome и Edge111+ (март 2023)
Firefox113+ (май 2023)
Opera98+
Samsung Internet22+
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().

Частые вопросы

Чем :nth-child(2 of .cls) отличается от .cls:nth-child(2)?

:nth-child(2 of .cls) – пре-фильтр: браузер сначала отбирает детей с классом .cls и берёт второго из них, где бы он ни стоял среди остальных. .cls:nth-child(2) – пост-фильтр: берётся второй ребёнок вообще, и правило сработает, только если у него есть класс .cls. Если второй ребёнок без класса, второй вариант не выберет ничего.

Можно ли передать в of несколько селекторов?

Да, S – это полноценный список селекторов через запятую: :nth-child(4 of .highlight, .sale) выберет четвёртый элемент среди детей, подходящих хотя бы под один из селекторов. Внутри S работают и псевдоклассы, например :not([hidden]).

Работает ли of S в :nth-of-type()?

Нет. Аргумент of S есть только у :nth-child() и :nth-last-child(). Он и не нужен: запись :nth-child(2 of li) с селектором тега полностью перекрывает возможности :nth-of-type() и расширяет их на любые селекторы.

Как сделать зебру таблицы, которая не ломается на скрытых строках?

Используйте пре-фильтр по видимости: tr:nth-child(even of :not([hidden])). Браузер нумерует только строки без атрибута hidden, поэтому при скрытии части строк фильтром чередование фона пересчитывается и остаётся ровным.

Какие браузеры поддерживают of S и нужен ли фолбэк?

Safari поддерживает с версии 9 (2015 год), Chrome и Edge – с 111 (март 2023), Firefox – с 113 (май 2023); статус Baseline 2023. Глобальная поддержка по caniuse на июль 2026 – около 92%. Не поддерживают IE и Opera Mini: если они заметны в вашей аудитории, оставьте обычное правило без of S запасным вариантом.

Как of S влияет на специфичность селектора?

По Selectors Level 4 :nth-child() – псевдокласс со специфичностью (0,1,0), и при наличии аргумента of S к ней добавляется специфичность самого специфичного селектора из списка S. То есть :nth-child(2 of #id) весит больше, чем :nth-child(2 of .cls).

Это и есть «nth-of-class», который давно просили разработчики?

По сути да. Отдельный псевдокласс :nth-of-class в спецификацию не вошёл, вместо него в Selectors Level 4 добавили аргумент of S. caniuse описывает его как обобщение :nth-of-type: то же самое, но фильтр не по тегу, а по произвольному селектору.

Материалы по теме

Валентина Меланина

Нужна консультация?

Разберу ваш сайт и покажу точки роста

Если хотите понять, как этот термин применить к вашему проекту — начнём с аудита.