Как назначить CSS-свойства селектору-родителю, в зависимости от существования элемента внутри него и управлять свойствами соседних HTML-элементов? Новый CSS-селектор :has()
поможет добраться до родителя определенного элемента с помощью только CSS. Например, если внутри контейнера есть изображение, этому контейнеру нужно добавить display: flex
.
Суть проблемы: задать стиль контейнеру-родителю в зависимости от существования элемента внутри него. Раньше это можно было сделать только с помощью Javascript или на уровне динамического создания шаблона. Т.е. создавать лишние CSS-классы и переключать их в зависимости от содержимого. Например:
В CSS для такого можно сделать что-то вроде:
/* Карточка с изображением */
.card {
display: flex;
align-items: center;
gap: 1rem;
}
/* Карточка без изображения */
.card--plain {
display: block;
border-top: 3px solid #7c93e9;
}
И в HTML:
<!-- Карточка с изображением -->
<div class="card">
<div class="card__image">
<img src="awameh.jpg" alt="">
</div>
<div class="card__content">
<!-- Текстовое описание -->
</div>
</div>
<!-- Карточка без изображения -->
<div class="card card--plain">
<div class="card__content">
<!-- Текстовое описание -->
</div>
</div>
Таким образом, чтобы поправить внешний вид карточкам без изображения, потребуется контейнеру-родителю .card
добавлять ещё CSS-класс .card--plain
.
Решить проблему с избыточными CSS-классами можно с помощью CSS-селектора :has()
:
.card:has(.card__image) {
display: flex;
align-items: center;
}
Проблема всё ещё может казаться надуманной, что стоит добавить контейнеру-родителю лишние пару CSS-классов. Но как выходить из ситуации с более сложными условиями? Вот примеры, когда помощь CSS-селектора :has()
уже не покажется лишней:
.card:has(h2 + p) {...}
Стиль будет применяться к контейнеру .card
при условии, когда внутри него за <H2>
будет следовать <P>
. Но не будет применяться, когда между <H2>
и <P>
появится какой-то другой (<HR>
в первой или <INPUT>
в последней карточках) элемент.
.card:has(h2 + input:focus) {...}
Стиль будет применяться к контейнеру .card
при условии, когда внутри него за <H2>
не только будет следовать <INPUT>
, но в это поле ещё будет помещён курсор (фокус в поле).
Поддержка CSS-селектора :has()
вашим браузером
Примеры использования CSS-селектора :has()
Наиболее интересно применение CSS-селектора :has()
при работе с формами и состояниями элементов управления в них. Например, можно показывать сообщения и кнопки в произвольном месте контейнера с формой.
Форма компонента фильтра
В этом примере есть форма-фильтр с несколькими параметрами. Когда ни один из них не отмечен, кнопка сброса отсутствует. Однако, когда хотя бы один отмечен, эту кнопку нужно показать.
Это не трудно сделать с помощью CSS-селектора :has
:
.btn-reset {
display: none;
}
.multiselect:has(input:checked) .btn-reset {
display: block;
}
Показывать/скрывать элементы формы по условию
Требуется показать конкретное поле формы на основе предыдущего ответа или выбора в выпадающем списке. С помощью CSS-селектора :has()
легко можно проверить, какая опция выбрана в меню и показать поле на основе этого выбора.
.other-field {
display: block;
}
form:has(option[value="other"]:checked) .other-field {
display: block;
}
При этом нет никакой необходимости волноваться об исходном порядке следования элементов в HTML.
Информационные модули
Когда в тестовом поле ввода есть ошибка, может понадобиться, чтобы заголовок контейнера тоже изменился и указывал на эту ошибку.
.module:has(.input-error) .headline {
color: #ca3131;
}
Элемент навигации с подменю
В этом примере показывается, как скрыть стрелку в зависимости от того, есть подменю или нет. Это тоже можно легко сделать с помощью CSS-селектора :has
. Достаточно проверить, содержится ли внутри <li>
элемент списка <ul>
. И если да – содержится, надо показать значок стрелки.
За стрелку у пунктов с подменю здесь отвечает:
li:has(ul) > a:after {
content: "";
/* стили для стрелки */
}
Без CSS-селектора :has
, потребовался бы класс для <li>
с подменю и способ управлять им. Что-то вроде такого:
.nav-item--with-sub > a:after {
content: "";
/* стили для стрелки */
}
Переключение цветовых схем
Пример использования CSS-селектора :has()
для изменения цветовой схемы веб-сайта. Если есть несколько тем, созданных с помощью CSS-переменных, ими можно управлять с помощью выпадающего списка <select>
.
html {
--color-1: #9e7ec8;
--color-2: #f1dceb;
}
И если выбрать другой вариант из списка, в зависимости от выбранной опции CSS-переменные можно изменить так:
html:has(option[value="blueish"]:checked) {
--color-1: #9e7ec8;
--color-2: #f1dceb;
}
Изменение сетки в зависимости от количества элементов
С CSS-grid можно использовать функцию minmax()
для создания действительно отзывчивых элементов сетки с автоматическим изменением размера. Однако этого может быть недостаточно. Например, нужно изменять сетку в зависимости от количества элементов.
.wrapper {
--item-size: 200px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--item-size), 1fr));
gap: 1rem;
}
Если появится пятый элемент, он будет перенесен в новую строку.
От переноса пятого элемента на новую строку легко избавиться, если проверить количество элементов внутри .wrapper
. Опять же, здесь используется концепция количественных запросов.
.wrapper:has(.item:nth-last-child(n + 5)) {
--item-size: 120px;
}
Изображение с подписью
В этом примере используется HTML-элемент <figure>
. Если внутри него есть <figcaption>
, стиль картинки должен немного отличаться:
- Добавится белый фон
- Немного обводки
- Уменьшатся изображение и
border-radius
figure:has(figcaption) {
padding: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 10px 0 rgba(#000, 0.1);
border-radius: 3px;
}
Заключение
Использование CSS-селектора :has()
позволяет во многих случаях, только с помощью CSS, легко и просто сделать вещи, которые требовали Javasript и динамическое добавление лишних CSS-классов для изменения стилей за пределами элемента-инициатора этих изменений.