Псевдокласс
:has()
Выбирает элемент, если внутри него или после него есть элемент, который подходит под селектор в скобках. Позволяет оформить родителя по содержимому.
Пример
<div class="card">
<div class="card__image">Картинка</div>
<p>Карточка с картинкой</p>
</div>
<div class="card">
<p>Карточка без картинки</p>
</div>.card {
margin-block-end: 0.5rem;
padding: 0.5rem;
border: 1px solid silver;
}
.card:has(.card__image) {
border-color: royalblue;
background-color: aliceblue;
}
.card__image {
padding: 0.5rem;
background-color: lightsteelblue;
}Результат
Синтаксис
:has(<относительные селекторы>) { … }
.card:has(img) { … } /* внутри есть img */
.card:has(> img) { … } /* img — прямой ребёнок */
h2:has(+ p) { … } /* сразу после идёт p */
h2:has(~ .note) { … } /* где-то дальше среди соседей есть .note */Значения
<относительные селекторы>- Селекторы через запятую. Каждый может начинаться с комбинатора: пробел (по умолчанию),
>,+или~. Он отсчитывается от элемента, к которому приписан:has(). Элемент подходит, если сработал хотя бы один селектор. Список не прощающий: ошибка в одном селекторе отключает всё правило.
Как понять
Обычные селекторы двигаются только вниз и вперёд: от предка к потомку, от элемента к следующему соседу. :has() разворачивает направление: стили получает элемент слева, а условие проверяется справа.
.card:has(img)— карточка, внутри которой есть картинка;h2:has(+ p)— заголовок, за которым сразу идёт абзац, то есть «предыдущий сосед»;body:has(dialog[open])— страница, на которой открыт диалог.
Условия комбинируются: :has(.a, .b) — есть хотя бы один из, :has(.a):has(.b) — есть оба. А :not(:has(img)) — внутри нет картинок.
Специфичность — как у :is(): по самому сильному селектору в скобках. У .card:has(img) — 0,1,1.
Ограничения: :has() нельзя вкладывать в :has(), а псевдоэлементы внутри не работают — такие правила браузер отбрасывает.
Примеры
Форма с ошибкой
Блок формы реагирует на состояние поля внутри: рамка краснеет, пока в поле неверный адрес.
<div class="field">
<label for="mail">Почта</label>
<input id="mail" type="email" value="не адрес">
</div>.field {
padding: 0.5rem;
border: 2px solid silver;
}
.field:has(:invalid) {
border-color: crimson;
background-color: mistyrose;
}Результат
Заголовок перед абзацем
Подзаголовок прижимается к тексту, только если за ним сразу идёт абзац:
<p class="title">Подзаголовок перед абзацем</p>
<p>Абзац</p>
<p class="title">Подзаголовок перед списком</p>
<ul><li>Пункт</li></ul>.title {
margin-block: 0 1rem;
font-weight: bold;
}
.title:has(+ p) {
margin-block-end: 0.25rem;
color: teal;
}Результат
Раскладка по количеству детей
:has() вместе с :nth-child() выбирает контейнер, в котором не меньше четырёх элементов:
<ul class="tags">
<li>Один</li>
<li>Два</li>
<li>Три</li>
<li>Четыре</li>
</ul>.tags:has(> :nth-child(4)) {
display: grid;
grid-template-columns: 1fr 1fr;
}Результат
Подсказки
:has()пересчитывается при каждом изменении внутри элемента. Широкие условия вродеbody:has(.x)на больших страницах замедляют работу, поэтому ставьте:has()на ближайший контейнер и уточняйте селектор внутри.- Для проверки поддержки используйте
@supports selector(:has(a)). Браузер без поддержки отбросит правило целиком, поэтому не смешивайте:has()с другими селекторами в одном списке. :has()нельзя вкладывать в:has(), но его можно писать внутри:is(),:not()и:where().
Поддержка браузерами
| Возможность | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS | Samsung Internet | Opera |
|---|---|---|---|---|---|---|---|---|
| :has() | 105 | 105 | 121 | 15.4 | 105 | 15.4 | 20.0 | 91 |
Яндекс Браузер работает на Chromium и поддерживает то же, что Chrome той же версии. Данные: MDN browser-compat-data 8.1.2, обновлено 17 сентября 2026 г.
Спецификация
См. также
- :not() Выбирает элементы, которые не подходят ни под один селектор из списка в скобках
- :is() Выбирает элемент, если он подходит хотя бы под один селектор из списка в скобках
- Соседний комбинатор Знак + выбирает элемент, который стоит сразу после указанного и у которого с ним общий родитель.
- Общий соседний комбинатор Знак ~ выбирает все элементы, которые идут после указанного на том же уровне, не обязательно вплотную.