:has селектор

:has — псевдокласс CSS, который выбирает элемент, если внутри него есть элемент, соответствующий переданному селектору; фактически первый «родительский» селектор в CSS.

Зачем нужно

До :has было невозможно стилизовать родительский элемент на основе его содержимого без JavaScript. :has открывает принципиально новые возможности: стилизовать форму если она содержит незаполненные обязательные поля, карточку если в ней есть изображение, параграф если за ним следует другой параграф.

Где используется

  • Стилизация родителя по дочернему содержимому
  • Формы: подсветка при наличии невалидных полей
  • Карточки: разные стили при наличии/отсутствии изображения
  • Навигация: выделение активного раздела
  • Замена JavaScript для условной стилизации

Синтаксис

/* Выбрать .card если внутри есть img */
.card:has(img) { ... }

/* Выбрать form если внутри есть :invalid */
form:has(:invalid) { ... }

/* Выбрать h2 если за ним идёт p (смежный сосед) */
h2:has(+ p) { ... }

/* Выбрать li если внутри есть a.active */
li:has(a.active) { ... }

/* Комбинирование с другими псевдоклассами */
.card:not(:has(img)) { ... } /* карточки БЕЗ изображения */

Примеры

Карточка с изображением — другой стиль

/* Без изображения */
.card {
  padding: 24px;
}

/* С изображением — убрать padding сверху */
.card:has(img) {
  padding-top: 0;
}

.card:has(img) img {
  border-radius: 8px 8px 0 0;
  width: 100%;
}

Форма с невалидными полями

/* Форма ошибки — красная рамка вокруг всей формы */
form:has(:invalid:not(:placeholder-shown)) {
  border: 2px solid #e44;
  border-radius: 8px;
}

/* Кнопка отправки заблокирована при наличии ошибок */
form:has(:invalid) button[type="submit"] {
  opacity: 0.5;
  pointer-events: none;
}
<form>
  <input type="email" required placeholder="Email">
  <button type="submit">Отправить</button>
</form>

Выделение активного пункта меню

/* li активен если внутри есть активная ссылка */
nav li:has(a.active) {
  background: rgba(0, 123, 255, 0.1);
  border-left: 3px solid #007bff;
}

Сетка без изображения — три колонки; с — две

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.grid:has(.featured-img) {
  grid-template-columns: 1fr 2fr; /* два блока если есть featured */
}

Поддержка браузеров

Chrome 105+, Safari 15.4+, Firefox 121+. Проверяйте актуальный статус: caniuse.com/css-has

/* Защита для старых браузеров через @supports */
@supports selector(:has(a)) {
  .card:has(img) { padding-top: 0; }
}

Частые ошибки

  1. :has с комплексными селекторами:has(.a .b) выбирает элемент у которого есть .b внутри .a внутри него — не самый читаемый код; держите аргументы простыми.
  2. Производительность — сложные аргументы :has могут влиять на производительность рендеринга; избегайте :has(*) или очень глубоких вложений.
  3. forgiving парсинг:has использует «forgiving» список: невалидный аргумент не ломает весь селектор, а игнорируется.

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

Ресурсы