Псевдокласс 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