Spec-Zone.ru › CSS

Псевдокласс CSS `:has()`

Базовая поддержка Широко доступно

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и во многих версиях браузеров. Она доступна во всех браузерах с декабря 2023 года.

Функциональный :has() CSS-псевдокласс выбирает элемент, если хотя бы один из относительных селекторов, переданных в качестве аргумента, совпадает хотя бы с одним элементом при привязке к этому элементу. Этот псевдокласс позволяет выбрать родительский элемент или предыдущий соседний элемент относительно опорного элемента, принимая в качестве аргумента список относительных селекторов.

/* Selects an h1 heading with a
paragraph element that immediately follows
the h1 and applies the style to h1 */
h1:has(+ p) {
  margin-bottom: 0;
}

Псевдокласс :has() имеет ту же специфичность, что и наиболее специфичный селектор среди его аргументов, как и :is() и :not().

Синтаксис

:has(<relative-selector-list>) {
  /* ... */
}

Если браузер не поддерживает сам псевдокласс :has(), весь блок селекторов не сработает, если только :has() не находится в списке селекторов с игнорированием ошибок, например в :is() и :where().

Псевдокласс :has() нельзя вкладывать в другой :has().

Псевдоэлементы также не являются допустимыми селекторами внутри :has() и не могут быть опорными элементами для :has(). Это связано с тем, что многие псевдоэлементы существуют только при определённых условиях, зависящих от стилей их предков, и возможность проверять их с помощью :has() может привести к циклическим запросам.

Примеры

Выбор родительского элемента

Возможно, вам нужен «комбинатор родителя», который позволяет подняться по дереву DOM и выбрать родителя определённого элемента. Псевдокласс :has() делает это с помощью parent:has(child) (для любого родителя) или parent:has(> child) (для непосредственного родителя). В этом примере показано, как задать стили для элемента <section>, если он содержит дочерний элемент с классом featured.

<section>
  <article class="featured">Featured content</article>
  <article>Regular content</article>
</section>
<section>
  <article>Regular content</article>
</section>
section:has(.featured) {
  border: 2px solid blue;
}

Результат

С комбинатором соседних элементов

Объявление стиля :has() в следующем примере изменяет отступ после заголовков <h1>, если сразу за ними следует заголовок <h2>.

HTML

<section>
  <article>
    <h1>Morning Times</h1>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </article>
  <article>
    <h1>Morning Times</h1>
    <h2>Delivering you news every morning</h2>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </article>
</section>

CSS

section {
  display: flex;
  align-items: start;
  justify-content: space-around;
}

article {
  display: inline-block;
  width: 40%;
}

h1,
h2 {
  font-size: 1.2em;
}

h2 {
  font-size: 1em;
  color: rgb(150 149 149);
}
h1,
h2 {
  margin: 0 0 1rem 0;
}

h1:has(+ h2) {
  margin: 0 0 0.25rem 0;
}

Результат

В этом примере рядом друг с другом показаны два похожих текста для сравнения: слева заголовок H1, за которым следует абзац, а справа заголовок H1, за которым следуют заголовок H2 и абзац. В примере справа :has() помогает выбрать элемент H1, за которым сразу следует элемент H2 (это обозначается комбинатором следующего соседа +), а правило CSS уменьшает отступ после такого элемента H1. Без псевдокласса :has() нельзя использовать селекторы CSS для выбора предшествующего соседнего элемента другого типа или родительского элемента.

Такое использование :has() аналогично опережающей проверке в регулярных выражениях, поскольку оба механизма позволяют выбирать элементы (или строки в регулярных выражениях) на основе того, что идёт после них. Это возможно, только если обработчик может «вернуться назад». В регулярном выражении abc(?=xyz) строка abc совпадает, только если сразу за ней следует строка xyz. Поскольку это опережающая проверка, xyz не включается в совпадение. Аналогичная конструкция в CSS — .abc:has(+ .xyz): она выбирает элемент .abc, только если за ним следует соседний элемент .xyz. Часть :has(+ .xyz) проверяет, следует ли за ним элемент .xyz, но затем выбирает предыдущий элемент .abc. Аналогичным образом можно реализовать отрицательную опережающую проверку, например abc(?!xyz), инвертировав :has(): .abc:not(:has(+ .xyz)) выбирает только элементы .abc, за которыми не следует .xyz (либо следующий соседний элемент не совпадает с .xyz, либо он является последним дочерним узлом).

С псевдоклассом :is()

Этот пример продолжает предыдущий и показывает, как выбрать несколько элементов с помощью :has().

HTML

<section>
  <article>
    <h1>Morning Times</h1>
    <h2>Delivering you news every morning</h2>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </article>
  <article>
    <h1>Morning Times</h1>
    <h2>Delivering you news every morning</h2>
    <h3>8:00 am</h3>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </article>
</section>

CSS

section {
  display: flex;
  align-items: start;
  justify-content: space-around;
}

article {
  display: inline-block;
  width: 40%;
}

h1 {
  font-size: 1.2em;
}

h2 {
  font-size: 1em;
  color: rgb(150 149 149);
}

h3 {
  font-size: 0.9em;
  color: darkgrey;
}
h1,
h2,
h3 {
  margin: 0 0 1rem 0;
}

:is(h1, h2, h3):has(+ :is(h2, h3, h4)) {
  margin: 0 0 0.25rem 0;
}

Результат

Здесь первый псевдокласс :is() используется для выбора любого заголовка из списка. Второй псевдокласс :is() используется для передачи списка селекторов следующих соседних элементов в качестве аргумента :has(). Псевдокласс :has() помогает выбрать любой элемент H1, H2 или H3, за которым сразу следует (обозначается +) элемент H2, H3 или H4, а правило CSS уменьшает отступ после таких элементов H1, H2 или H3.

Этот селектор также можно было бы записать так:

:is(h1, h2, h3):has(+ h2, + h3, + h4) {
  margin: 0 0 0.25rem 0;
}

Логические операции

Реляционный селектор :has() можно использовать, чтобы проверить, истинно ли одно из нескольких условий или истинны ли все условия.

Если указать значения через запятую внутри реляционного селектора :has(), будет проверяться, существует ли хотя бы один из параметров. x:has(a, b) задаст стиль для x, если существует потомок a ИЛИ b.

Если объединить несколько реляционных селекторов :has(), будет проверяться, существуют ли все параметры. x:has(a):has(b) задаст стиль для x, если существуют потомки a И b.

body:has(video, audio) {
  /* styles to apply if the content contains audio OR video */
}
body:has(video):has(audio) {
  /* styles to apply if the content contains both audio AND video */
}

Особенности производительности

Некоторые способы использования псевдокласса :has() могут значительно снизить производительность страницы, особенно при динамических обновлениях (изменениях DOM). При изменении DOM браузерным движкам приходится повторно вычислять селекторы :has(), а сложные или недостаточно ограниченные селекторы могут приводить к дорогостоящим вычислениям.

Избегайте широкого выбора опорного элемента

Опорным селектором (A в A:has(B)) не должен быть элемент с большим количеством потомков, например body, :root или *. Привязка :has() к слишком общим селекторам может ухудшить производительность, поскольку при любом изменении DOM внутри всего поддерева широко выбранного элемента браузеру нужно повторно проверять условие :has().

/* Avoid anchoring :has() to broad elements */
body:has(.sidebar) {
  /* styles */
}
:root:has(.content) {
  /* styles */
}
*:has(.item) {
  /* styles */
}

Вместо этого привязывайте :has() к конкретным элементам, например .container или .gallery, чтобы ограничить область проверки и повысить производительность.

/* Use specific containers to limit scope */
.container:has(.sidebar-expanded) {
  /* styles */
}
.content-wrapper:has(> article[data-priority="high"]) {
  /* styles */
}
.gallery:has(> img[data-loaded="false"]) {
  /* styles */
}

Сведите к минимуму обходы поддеревьев

Внутренний селектор (B в A:has(B)) должен использовать комбинаторы, например > или +, чтобы ограничить обход. Если селектор внутри :has() недостаточно ограничен, браузеру может потребоваться при каждом изменении DOM обходить всё поддерево опорного элемента, чтобы проверить, выполняется ли условие.

В этом примере любое изменение внутри .ancestor требует проверки всех потомков на наличие .foo:

/* May trigger full subtree traversal */
.ancestor:has(.foo) {
  /* styles */
}

Использование комбинаторов дочерних или соседних элементов ограничивает область действия внутреннего селектора и снижает стоимость изменений DOM. В этом примере браузеру нужно проверять только непосредственных потомков или потомков конкретного соседнего элемента:

/* More constrained - limits traversal */
.ancestor:has(> .foo) {
  /* direct child */
}
.ancestor:has(+ .sibling .foo) {
  /* descendant of adjacent sibling */
}

Некоторые внутренние селекторы могут заставить браузер при каждом изменении DOM подниматься по цепочке предков в поисках потенциальных опорных элементов, которые, возможно, потребуется обновить. Это происходит, когда из структуры следует, что нужно проверить предков изменённого элемента.

В этом примере при любом изменении DOM необходимо проверить, является ли изменённый элемент каким-либо элементом (*), который непосредственно вложен в .foo, и является ли его родительский элемент (или один из его предков) элементом .ancestor.

/* Might trigger ancestor traversal */
.ancestor:has(.foo > *) {
  /* styles */
}

Ограничение внутреннего селектора конкретными классами или комбинаторами непосредственных дочерних элементов (например, .specific-child в следующем фрагменте) сокращает дорогостоящие обходы цепочки предков: браузер проверяет только чётко определённый элемент, что повышает производительность.

/* Constrain the inner selector to avoid ancestor traversals */
.ancestor:has(.foo > .specific-child) {
  /* styles */
}

Примечание: Производительность может улучшиться по мере оптимизации браузерами реализации :has(), но основные ограничения сохраняются: :has() требует обхода всего поддерева, поэтому нужно минимизировать его размер. В селекторе вида A:has(B) убедитесь, что у A не слишком много потомков, а B достаточно ограничен, чтобы избежать ненужного обхода.

Спецификации

Спецификация
Selectors Level 4
# relational

Совместимость с браузерами

Компьютеры Мобильные устройства
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на iOS Samsung Internet WebView Android WebView на iOS
has
105
105
121
91
15.4
105
121
72
15.4
20.0
105
15.4

См. также

  • :is(), :where(), :not()
  • Селекторы и комбинаторы CSS
  • Структура селектора CSS
  • Список селекторов
  • Модуль селекторов CSS
  • Выбор и обход дерева DOM

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API