В этом отрывке из Unleashing the Power of CSS мы углубимся в то, как выбирать элементы с помощью :has()селектора CSS.
Провозглашенный «родительским селектором», :has()псевдокласс имеет гораздо больший диапазон, чем просто стилизация предка элемента. С его доступностью в Safari 15.4+ и Chromium 105+, а также с флагом в Firefox, это прекрасное время для вас, чтобы ознакомиться с :has()вариантами его использования.
Основная функциональность псевдокласса :has()заключается в стилизации элемента, к которому он присоединен, иначе известного как «целевой» элемент. Это похоже на другие псевдоклассы, такие как :hoverили :active, где a:hoverпредназначен для стилизации <a>элемента в активном состоянии.
Однако :has()он также похож на :is(), :where()и :not(), поскольку принимает список относительных селекторов в скобках. Это позволяет :has()создавать сложные критерии для тестирования, что делает его очень мощным селектором.
Чтобы понять, как это :has()работает, давайте рассмотрим пример его применения. В следующем селекторе мы проверяем, <article>есть ли у элемента <img>дочерний элемент:
article:has(img) {}
Возможный результат этого селектора показан на изображении ниже. Показаны три элемента статьи, два из которых содержат изображения, оба имеют бледно-зеленый фон и отступы, отличные от элемента без изображения.
Приведенный выше селектор будет применяться до тех пор, пока <img>элемент существует где-либо вместе с <article>элементом — будь то прямой дочерний элемент или потомок других вложенных элементов.
Если мы хотим убедиться, что правило применяется только в том случае, если является <img>прямым (не вложенным) дочерним элементом элемента <article>, мы также можем включить дочерний комбинатор:
article:has(> img) {}
Результат этого изменения показан на изображении ниже. Показаны те же три карточки, но на этот раз только та, где изображение является прямым дочерним элементом, <article>имеет бледно-зеленый фон и отступы.
В обоих селекторах определяемые нами стили применяются к целевому элементу, то есть файлу <article>. Вот почему люди часто называют :has()«родительский» селектор: если определенные элементы существуют определенным образом, их «родительский» получает назначенные стили.
Примечание: :has()сам псевдокласс не добавляет веса специфичности селектору. Подобно :is()и :not(), специфичность :has()равна селектору с наивысшей специфичностью в списке селекторов. Например, :has(#id, p,.class)будет иметь специфичность, предоставляемую файлу id. Чтобы освежить в памяти специфичность, просмотрите раздел о специфичности в CSS Master, 3rd Edition.
Мы также можем выбрать целевой элемент, если за ним следует определенный одноуровневый элемент, используя соседний одноуровневый комбинатор ( +). В следующем примере мы выбираем <h1>элемент, только если за ним непосредственно следует <h2>:
h1:has(+ h2) {}
На изображении ниже <article>показаны два элемента. В первом случае, поскольку <h1>за ним следует <h2>, <h1>к нему применяется бледно-зеленый фон.
Используя общий комбинатор братьев и сестер ( ~), мы можем проверить, является ли конкретный элемент родственным элементом в любом месте, следующем за целью. Здесь мы проверяем, есть ли <p>элемент где-то в качестве родственного элемента <ul>:
ul:has(~ p) {}
На изображении ниже показаны два <article>элемента, каждый из которых содержит неупорядоченный список. За списком второй статьи следует абзац, поэтому к нему применен бледно-зеленый фон.
Селекторы, которые мы использовали до сих пор, определяли стиль целевого элемента, прикрепленного к :has(), например, <ul>in ul:has(~ p). Как и в случае с обычными селекторами, наши :has()селекторы могут быть расширены, чтобы сделать их более сложными, например, установить условия стиля для элементов, не связанных напрямую с селектором :has().
В следующем селекторе стили применяются к любым <p>элементам, являющимся одноуровневыми элементами <h2>, у которых есть <h3>соседний одноуровневый элемент:
h2:has(+ h3) ~ p
На изображении ниже <article>показаны два элемента. Во втором случае абзацы имеют бледно-зеленый фон и увеличенное левое поле, потому что абзацы являются родственными по <h2>отношению к <h3>.
Примечание: мы добьемся большего успеха, :has()если хорошо разберемся в доступных селекторах CSS. MDN предлагает краткий обзор селекторов, и я написал серию из двух частей о селекторах с дополнительными практическими примерами.
Помните, :has()может принимать список селекторов, которые мы можем рассматривать как ORусловия. Давайте выделим абзац, если он включает <a>_or_ <strong>_or_ <em>:
p:has(a, strong, em) {}
На изображении ниже есть два абзаца. Поскольку второй абзац содержит <strong>элемент, он имеет бледно-зеленый фон.
Мы также можем связать :has()селекторы для создания ANDусловий. В следующем составном селекторе мы проверяем, что an <img>является первым дочерним элементом <article>, и что он <article>содержит <h1>, за которым следует <h2>:
article:has(> img:first-child):has(h1 + h2) {}
На изображении ниже показаны три <article>элемента. Вторая статья имеет бледно-зеленый фон (наряду с другими стилями), потому что она содержит как изображение в качестве первого дочернего элемента, так и <h1>файл <h2>.