До недавнего времени в CSS действовало жёсткое правило: стилизовать элемент по его содержимому нельзя. Вы могли выбрать потомка, зная родителя, но не наоборот — «покрасить карточку, если внутри неё есть картинка» было невозможно, и приходилось добавлять специальные классы через JavaScript. Селектор :has() снял это ограничение. Теперь можно выбрать родитель по тому, что находится внутри, — «выбери форму, содержащую ошибку», «выдели карточку с бейджем», «подсвети поле, у которого рядом есть иконка». Это открывает целый класс задач, которые раньше решались только скриптами.
После этого урока вы сможете выбирать и стилизовать элементы в зависимости от их содержимого — на чистом CSS, без JavaScript и без лишних классов в разметке.
Что уже умеем / что добавим
Вы умеете выбирать элементы по состоянию, положению, содержимому-декору и хранить значения в переменных. Но выбрать родитель по его содержимому вы пока не могли. Теперь добавим :has() — «родительский» селектор. В сквозном проекте мы выделим карточку услуги, внутри которой есть метка «Хит», не помечая её отдельным классом.
Что вы узнаете в рамках статьи
- Что делает селектор
:has()и почему его называют «родительским». - Как выбрать элемент по наличию определённого потомка.
- Как комбинировать
:has()с другими селекторами и состояниями. - Какие практические задачи он решает без JavaScript.
Ключевые термины
:has()— псевдокласс, выбирающий элемент по наличию подходящего содержимого.- Родительский селектор — селектор, выбирающий элемент на основе его потомков.
- Условие внутри
:has()— селектор, который должен найтись внутри элемента. - Комбинация селекторов — совмещение
:has()с классами, состояниями и соседями.
Что такое :has() и почему он «родительский»
:has() — это псевдокласс, который проверяет, есть ли внутри элемента что-то, подходящее под указанный внутри селектор. Если есть — правило применяется к самому внешнему элементу. Читается это как «элемент, который имеет внутри …»:
.card:has(img) {
padding: 0;
}
Правило применится к каждой карточке .card, внутри которой есть тег img. Обратите внимание: стилизуется именно карточка (внешний элемент), а условием служит её содержимое (img). Раньше это было невозможно — CSS умел идти только «вниз», от родителя к потомку. :has() позволяет посмотреть «внутрь» и принять решение о родителе. Поэтому его давно ждали и называли «родительским селектором».
Выбор по наличию потомка
Самый частый сценарий — применить стиль к контейнеру, если в нём есть определённый элемент. Например, убрать отступ у абзаца, если следом идёт список, или выделить блок с изображением:
.field:has(.error) {
border-color: #e0473e;
}
Здесь поле формы .field получит красную рамку, только если внутри него есть элемент с классом .error (сообщение об ошибке). Не нужно добавлять классу поля модификатор через скрипт — CSS сам увидит ошибку внутри и подсветит поле. Это очень удобно для форм: наличие ошибки, подсказки или обязательной метки внутри поля меняет вид всего поля автоматически.
Комбинирование с другими селекторами
:has() становится по-настоящему мощным в связке с другими селекторами и состояниями. Внутри скобок можно использовать любые селекторы, а сам :has() — присоединять к классам, псевдоклассам и комбинаторам соседства:
/* Карточка, внутри которой есть бейдж */
.card:has(.badge) {
outline: 2px solid #2d6cdf;
}
/* Метка рядом с полем в фокусе */
.field:has(input:focus) {
background: #f0f6ff;
}
Первое правило обводит любую карточку, содержащую бейдж. Второе — подсвечивает поле, когда вложенный input в фокусе: :has() внутри содержит другой псевдокласс (:focus), и вместе они дают «родитель, чей потомок в фокусе». Такой приём заменяет то, что раньше требовало JavaScript-обработчиков. Можно комбинировать и с соседями: h2:has(+ p) выберет заголовок, за которым следует абзац.
Практические сценарии без JavaScript
Ценность :has() в том, что он убирает целый пласт мелких скриптов. Вот типичные задачи, которые теперь решаются одним селектором:
/* Убрать нижнюю рамку у пункта меню, содержащего подменю */
.menu-item:has(.submenu) {
border-bottom: none;
}
Раньше, чтобы отличить пункты с подменю, разметку помечали классом вручную или через скрипт. С :has() CSS сам определяет такие пункты по содержимому. Похожим образом делают: подсветку строки таблицы с отмеченным чекбоксом (tr:has(input:checked)), особый вид галереи с одной картинкой против нескольких, реакцию контейнера на пустое или заполненное поле. Общий принцип один: если оформление зависит от того, что находится внутри, :has() почти наверняка справится без единой строки JavaScript.
Применяем в проекте
Выделим карточки услуг, внутри которых есть метка «Хит», — обведём их акцентной рамкой, чтобы популярные услуги бросались в глаза. Нам не нужен отдельный класс на «горячих» карточках: :has() найдёт их по наличию метки.
.card {
border: 1px solid #e5e7eb;
}
.card:has(.badge) {
border-color: var(--accent);
box-shadow: 0 6px 16px rgba(45, 108, 223, 0.15);
}
Обновите страницу: карточки с меткой «Хит» получили заметную акцентную рамку и лёгкую подсветку, а обычные остались нейтральными. Мы не добавляли этим карточкам никакого специального класса — CSS сам отличил их по содержимому. Стоит убрать метку из карточки, и выделение исчезнет автоматически. Это чистое, декларативное решение: вид зависит от содержимого, и всё описано в CSS.
Частые ошибки
- Ждать выбор потомка.
:has()стилизует внешний элемент, а не то, что внутри скобок; условие — это содержимое, а цель — сам элемент. - Лишний пробел.
Псевдокласс присоединяют к элементу слитно:.card:has(.badge), а не.card :has(...). - Слишком тяжёлые условия.
Очень сложные:has()на больших списках могут подтормаживать; держите условия простыми. - Считать, что нужен JavaScript.
Многие задачи «стилизовать по содержимому» теперь решает:has()— прежде чем писать скрипт, проверьте, не хватит ли селектора.
Итоги
Прекрасная работа — вы освоили один из самых долгожданных селекторов CSS! Теперь вы умеете выбирать элемент по его содержимому через :has(), применять стиль к родителю по наличию нужного потомка, комбинировать :has() с классами и состояниями и решать задачи, которые раньше требовали JavaScript. В сквозном проекте вы выделили популярные карточки по наличию метки — без единого нового класса.
Отличный прогресс — ваш CSS стал по-настоящему «умным»! В следующем уроке мы разберём функцию calc(), которая позволяет вычислять значения прямо в стилях — складывать, вычитать и комбинировать разные единицы.