Псевдокласс :has()

Определение

:has() – реляционный CSS-псевдокласс из Selectors Level 4: выбирает элемент по наличию потомка, соседа или состояния внутри. Долгожданный «родительский селектор» работает во всех браузерах с декабря 2023 (Firefox 121).

3 мин 35 Обновлено 5 сентября

Запрос на «родительский селектор» – возможность стилизовать элемент в зависимости от его содержимого – разработчики повторяли больше десяти лет: CSS умел выбирать потомков по родителю, но не наоборот. Псевдокласс :has() из спецификации Selectors Level 4 закрыл эту потребность и оказался заметно шире. Формально это реляционный псевдокласс: он проверяет условие относительно элемента – наличие потомка, следующего соседа, состояние поля формы – и применяет стили, только когда условие выполнено. В паре с псевдоклассом отрицания :not() он закрывает сценарии, ради которых раньше подключали JavaScript и навешивали классы-флаги.

Как это работает

Синтаксис – селектор:has(аргумент). Браузер «якорит» проверку на элементе слева: section:has(img) выберет секцию, внутри которой на любой глубине есть изображение; сама картинка при этом не стилизуется. Аргумент – список относительных селекторов через запятую, причём список forgiving («прощающий»): невалидный селектор в нём игнорируется и не ломает правило.

Комбинаторы внутри аргумента меняют смысл проверки:

  • :has(img) – потомок на любой глубине;
  • :has(> img) – только прямой потомок;
  • h1:has(+ h2) – «предыдущий сосед»: выбирается заголовок, сразу за которым идёт h2;
  • :has(video, audio) – логическое ИЛИ; цепочка :has(video):has(audio) – И.

Типовые применения в одном блоке кода:

/* Родитель по ребёнку: карточка с изображением и без */
.card:has(.card__image) { display: flex; gap: 1rem; }
.card:not(:has(.card__image)) { border-top: 3px solid #7c93e9; }

/* Подсветка группы полей формы при ошибке */
.form-group:has(:invalid) { border-color: #d33; }

/* Quantity query: от 5 элементов – плитка мельче */
.wrapper:has(.item:nth-last-child(n + 5)) { --item-size: 120px; }

Последний пример – так называемый quantity query: связка с :nth-last-child() меняет стили контейнера в зависимости от количества дочерних элементов. До :has() это делалось только скриптом.

:has() и классы-флаги из JavaScript – в чём разница

Стилизация родителя: :has() против JS-классов
Критерий:has()JS + классы-флаги
Логикаживёт в CSS, одно правилоразнесена между скриптом и стилями
Разметкачистая, без модификаторовслужебные классы вида has-image
Изменения DOMбраузер пересчитывает стили самнужен MutationObserver или повторный вызов
Отказоустойчивостьработает без JavaScriptломается, если скрипт упал

Ограничения зафиксированы в спецификации: вкладывать :has() внутрь :has() нельзя, а псевдоэлементы вроде ::before невалидны и внутри аргумента, и как якорь. В браузере без поддержки селектор с :has() инвалидирует всё правило (вне forgiving-обёрток :is() и :where()), поэтому для прогрессивного улучшения используют проверку @supports selector(:has(a)).

Специфичность :has() равна специфичности самого «сильного» селектора в аргументе – сам псевдокласс ничего не добавляет. div:has(#promo) весит как селектор с ID. Как это влияет на каскад – в статье о специфичности CSS.

Зачем это нужно

Практическая ценность – меньше JavaScript и чище разметка. До :has() реакция родителя на содержимое требовала скрипта, который следил за DOM и переключал классы; теперь то же самое делает браузер. Типовые задачи:

  • навигация – подсветить пункт меню, содержащий активную ссылку: li:has(.current);
  • карточки – разные макеты для карточки с изображением и без, без модификаторов в разметке;
  • формы – стилизация label и целых групп по состояниям :checked, :invalid, :focus вложенных полей;
  • глобальные состояния – реакция страницы на виджет: :root:has([aria-expanded="true"]), удобно в связке с aria-expanded.

Цена вопроса – производительность: браузеру приходится обходить поддерево якорного элемента при каждой перепроверке. Рекомендации MDN и команды Chrome: не якорить :has() на body, :root или универсальном селекторе * и сужать область проверки комбинаторами > и +.container:has(> img) вместо .container:has(img).

Поддержка :has() браузерами
БраузерВерсияДата
Safari15.4март 2022, первый браузер с поддержкой
Chrome, Edge105август 2022
Firefox121декабрь 2023
BaselineNewly availableдекабрь 2023
BaselineWidely availableориентировочно июнь 2026, через 30 месяцев

Глобальная поддержка по caniuse – около 92,7% на июль 2026. Не поддерживают :has() только IE и Opera Mini. Если проект обязан работать в устаревших браузерах, базовые стили пишут без :has(), а улучшения заворачивают в блок @supports selector(:has(a)) – тогда правило с неподдерживаемым селектором не инвалидирует остальной CSS.

Пример

Классическая задача форм: показать дополнительное поле, когда пользователь выбрал в списке вариант «Другое».

.other-field { display: none; }

form:has(option[value="other"]:checked) .other-field {
  display: block;
}

Раньше здесь понадобился бы обработчик change, который навешивает класс на форму. Одно CSS-правило заменяет обработчик события и продолжает работать, даже если JavaScript отключён или не успел загрузиться. По тому же принципу подсвечивают подпись активного поля – label:has(+ input:focus).

В практике с 1С-Битрикс :has() экономит время на типовых компонентах: они генерируют фиксированную разметку, и чтобы добавить класс-модификатор, шаблон компонента пришлось бы копировать и править. С :has() обёртку стилизуют по содержимому – например, прячут блок фильтра, в котором не осталось ни одного пункта, – одним правилом в CSS, без правки template.php.

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

Правда ли, что :has() – это «родительский селектор» в CSS?

Да, это главный сценарий: section:has(img) стилизует родителя по наличию потомка. Но :has() шире – это реляционный псевдокласс: он проверяет и следующего соседа (h1:has(+ h2)), и состояние полей формы, и количество дочерних элементов. Родительский селектор – лишь частный случай.

Как выбрать элемент, за которым идёт определённый сосед?

Через комбинатор соседа внутри аргумента: h1:has(+ h2) выберет заголовок, сразу за которым идёт h2. До появления :has() CSS умел стилизовать только следующий элемент по предыдущему, обратное направление было недоступно.

Можно ли вкладывать :has() внутрь :has()?

Нет, спецификация Selectors Level 4 прямо запрещает вложенность :has() в :has(). Псевдоэлементы ::before и ::after внутри аргумента тоже невалидны. Сложные условия собирают цепочкой: .a:has(.b):has(.c) – логическое И.

Какая специфичность у псевдокласса :has()?

Сам :has() специфичности не добавляет: берётся специфичность самого специфичного селектора в его аргументе, как у :is() и :not(). Например, div:has(#promo) весит как селектор с ID – это стоит учитывать, чтобы не получить трудноперебиваемые правила.

Можно ли использовать :has() в продакшене в 2026 году?

Да. Safari поддерживает его с версии 15.4 (март 2022), Chrome и Edge – со 105 (август 2022), Firefox – со 121 (декабрь 2023). Статус Baseline Newly available присвоен в декабре 2023, глобальная поддержка по caniuse – около 92,7%. Для старых браузеров есть проверка @supports selector(:has(a)).

Влияет ли :has() на производительность страницы?

Может влиять: браузер обходит поддерево якорного элемента при каждой перепроверке стилей. Рекомендации MDN и команды Chrome – не якорить :has() на body, :root или универсальном селекторе * и сужать проверку комбинаторами > и +, например .container:has(> img) вместо .container:has(img).

Как выбрать элементы, внутри которых чего-то нет?

Комбинацией с :not(): селектор figure:not(:has(figcaption)) выберет все figure без подписи. Это приём «отрицание по потомку», который до :has() средствами CSS был невозможен – требовался JavaScript.

Связанные термины

Selectors Level 4 Selectors Level 4 – модуль спецификации CSS от W3C, расширивший селекторы: :is(), :where(), :has(), :not() со списком, :nth-child(An+B of S). Статус – Working Draft (редакция 22.01.2026), но ядро давно поддержано всеми браузерами. Псевдокласс :not() :not() – CSS-псевдокласс отрицания: выбирает элементы, не соответствующие селектору-аргументу. В Selectors Level 4 принимает список селекторов (:not(.a, .b)) – кроссбраузерно с января 2021 (Chrome 88, Firefox 78, Safari 9). Псевдокласс :nth-last-child() :nth-last-child() – CSS-псевдокласс, выбирающий элементы по позиции среди соседей с отсчётом от конца родителя: nth-last-child(1) – последний элемент. Принимает те же аргументы, что :nth-child(): число, odd/even, формулу An+B. Специфичность селектора Специфичность селектора – механизм каскада CSS, определяющий, какое из конфликтующих правил применит браузер. Считается триадой A-B-C (ID, классы, теги) и сравнивается попозиционно: один ID тяжелее любого числа классов.

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

Что почитать дальше по этой теме

Статья Что боты находят на сайте, а вы – нет Боты ходят по изнанке сайта, куда живой человек не забредёт, и фиксируют в логах то, чего не видно ни в Метрике, ни на витрине: падающие с ошибкой 500 страницы каталога, битый файл с 747 обращениями, поток сканеров секретов. Разбираю на реальных логах, что искать и как чинить. 7 мин 217 22 июля 2026 Инструкция Как выгрузить и читать access-логи, если хостинг хранит их три дня Access-лог – единственное место, где виден весь трафик сайта, включая ИИ-ботов, которых не замечают счётчики. Разбираю на практике: где взять логи, как не потерять их из-за ротации, какие команды показывают ботов, ошибки и подозрительную активность. 7 мин 173 21 июля 2026 Флагманский гайд AI-анализ email-обращений: методика на стыке Яндекс.Метрики и Claude Code Классический email-трекинг отвечает на вопрос «откуда пришло обращение». AI-слой отвечает на вопросы, которые раньше требовали ручного аналитика: значим ли email на фоне звонков, форм и мессенджеров, почему один источник даёт качественные обращения, а другой – пустые, какие сегменты визитов предшествуют письму, что написать в ответ с учётом пути пользователя. Здесь – как я собираю это на уже имеющемся стеке: Logs API Метрики, PostgreSQL и Claude Code, без новых платных сервисов. 11 мин 297 26 мая 2026 Флагманский гайд Майкор: ИИ-аудит проекта по 4 точкам контакта Майкор – это перекрёстный ИИ-аудит проекта в 4 точках контакта: сайт, контекстная реклама, AI-поиск и Яндекс.Карты. Я анализирую каждую систему и смотрю связки между ними – где маркетинг рассказывает одно, реклама ведёт на другое, а AI-системы цитируют третий номер телефона. В одном из моих аудитов медицинской клиники в Москве у организации в индексе AI-сервисов оказалось 6 разных номеров и всего 7 упоминаний на 64 проверочных запроса – при том, что в обычной выдаче Яндекса клиника была в топ-1. Майкор закрывает такие разрывы за один аудит вместо четырёх раздельных. 16 мин 502 13 мая 2026 Гайд FAQPage Schema: как разметить блок частых вопросов и попасть в AI-ответы FAQPage schema – один из самых эффективных типов разметки для попадания в AI-ответы. Страницы с FAQPage schema цитируются AI-поисковиками в 2,7 раза чаще. В этом гайде – формат разметки, правила написания ответов для AI, реализация на 1С-Битрикс и типичные ошибки, которые обнуляют эффект. 8 мин 335 25 апреля 2026