The :has() style declaration in the following example adjusts the spacing after headings if they are immediately followed by an heading.
This example shows two similar texts side-by-side for comparison – the left one with an H1 heading followed by a paragraph and the right one with an H1 heading followed by an H2 heading and then a paragraph. In the example on the right, :has() helps to select the H1 element that is immediately followed by an H2 element (indicated by the adjacent sibling combinator + ) and the CSS rule reduces the spacing after such an H1 element. Without the :has() pseudo-class, you cannot use CSS selectors to select a preceding sibling of a different type or a parent element.
- With the :is() pseudo-class
- HTML
- CSS
- Result
- Logical operations
- Analogy between :has() and regular expressions
- Positive lookahead (?=pattern)
- Negative lookahead (?!pattern)
- Specifications
- Browser compatibility
- See also
- Found a content problem with this page?
- The CSS :has Selector (and 4+ Examples)
- Использование CSS-селектора :has() на примерах
- Как использовать CSS-селектор :has()?
- Совместимость :has() с браузерами
- CSS-селектор :has() на практике
With the :is() pseudo-class
This example builds on the previous example to show how to select multiple elements with :has() .
HTML
section> article> h1>Morning Timesh1> h2>Delivering you news every morningh2> 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 Timesh1> h2>Delivering you news every morningh2> h3>8:00 amh3> 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; >
Result
Here, the first :is() pseudo-class is used to select any of the heading elements in the list. The second :is() pseudo-class is used to pass a list of adjacent sibling selectors as an argument to :has() . The :has() pseudo-class helps to select any H1 , H2 , or H3 element that is immediately followed by (indicated by + ) an H2 , H3 , or H4 element and the CSS rule reduces the spacing after such H1 , H2 , or H3 elements.
This selector could have also been written as:
:is(h1, h2, h3):has(+ h2, + h3, + h4) margin: 0 0 0.25rem 0; >
Logical operations
The :has() relational selector can be used to check if one of the multiple features is true or if all the features are true.
By using comma-separated values inside the :has() relational selector, you are checking to see if any of the parameters exist. x:has(a, b) will style x if descendant a OR b exists.
By chaining together multiple :has() relational selectors together, you are checking to see if all of the parameters exist. x:has(a):has(b) will style x if descendant a AND b exist.
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 */ >
Analogy between :has() and regular expressions
Interestingly, we can relate some CSS :has() constructs with the lookahead assertion in regular expressions because they both allow you to select elements (or strings in regular expressions) based on a condition without actually selecting the condition matching the element (or string) itself.
Positive lookahead (?=pattern)
In the regular expression abc(?=xyz) , the string abc is matched only if it is immediately followed by the string xyz . As it is a lookahead operation, the xyz is not included in the match.
The analogous construct in CSS would be .abc:has(+ .xyz) : it selects the element .abc only if there is an adjacent sibling .xyz . The part :has(+ .xyz) acts as a lookahead operation because the element .abc is selected and not the element .xyz .
Negative lookahead (?!pattern)
Similarly, for the negative lookahead case, in the regular expression abc(?!xyz) , the string abc is matched only if it is not followed by xyz . The analogous CSS construct .abc:has(+ :not(.xyz)) doesn’t select the element .abc if the next element is .xyz .
Specifications
Browser compatibility
BCD tables only load in the browser
See also
Found a content problem with this page?
This page was last modified on Jul 18, 2023 by MDN contributors.
Your blueprint for a better internet.
The CSS :has Selector (and 4+ Examples)
The CSS :has selector helps you select elements that contain elements that match the selector you pass into the :has() function. It’s essentially a “parent” selector, although far more useful than just that. For example, imagine being able to select all
. That’s what we can do:
Although it’s not supported in any browser as I write, it has now dropped in Safari Technical Preview 137, so it’s starting to happen! Pretty darn handy right! Here’s another example. Say you want space after your headers. Of course! A bit of margin-block-end on your h2 should do it. But… what if there is a subtitle? Now we can select a parent on the condition a subtitle is present and adjust the spacing.
h2, .subtitle < margin: 0 0 1.5rem 0; >.header-group:has(h2):has(.subtitle) h2 < margin: 0 0 0.2rem 0; /* reduce spacing on header, because subtitle will handle it */ >/* Blog Post Title
Blog Post Title
This is a subtitle */
The way I think about :has is this: it’s a parent selector pseudo-class. That is CSS-speak for “it lets you change the parent element if it has a child or another element that follows it.” This might feel weird! It might break your mental model of how CSS works. This is how I’m used to thinking about CSS:
You can only style down, from parent to child, but never back up the tree. :has completely changes this because up until now there have been no parent selectors in CSS and there are some good reasons why. Because of the way in which browsers parse HTML and CSS, selecting the parent if certain conditions are met could lead to all sorts of performance concerns. If I sit down and think about all the ways I might use :has today, I sort of get a headache. It would open up this pandora’s box of opportunities that have never been possible with CSS alone. Another example: let’s say we want to only apply styles to links that have images in them:
This would be helpful from time to time. I can also see :has being used for conditionally adding margin and padding to elements depending on their content. That would be neat. The :has selector is part of the CSS Selectors Level 4 specification which is the same spec that has the extremely useful :not pseudo-class. You could argue that the CSS :has selector is more powerful than just a “parent” selector, which is exactly what Bramus has done! Like in the subheadings example above, you aren’t necessarily ultimately selecting the parent, you might select the parent in a has-condition, but then ultimately select a child element from there.
/* Matches elements that have a as a child element */ figure:has(figcaption) < … >/* Matches elements that is a child of a that contains a child element */ figure:has(figcaption) img
There you can quickly see that the second selector is selecting a child , not just the parent of the .
And the list is forgiving: The list is no longer “forgiving” after the W3C adopted a resolution in December 2020 in response to a reported issue. So, if the selector list contains even one invalid argument, the entire list is ignored:
/* 👎 */ article:has(h2, ul, ::-blahdeath) < /* ::blahdeath is invalid, making the entire selector invalid. */ >
/* 👍 */ article:has(:where(h2, ul, ::-blahdeath)) < /* :where is a forgiving selector, making this valid. */ >
The :not() selector is part of the same spec…
Unlike :has , :not does have pretty decent browser support and I used it for the first time the other day:
That’s great I also love how gosh darn readable it is; you don’t ever have to have seen this line of code to understand what it does. Another way you can use :not is for margins:
So every element that is not the last item gets a margin. This is useful if you have a bunch of elements in a card, like this:
CSS Selectors Level 4 is also the same spec that has the :is selector that can be used like this today in a lot of browsers:
:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) < color: #BADA55; >/* . which would be the equivalent of: */ section h1, section h2, section h3, section h4, section h5, section h6, article h1, article h2, article h3, article h4, article h5, article h6, aside h1, aside h2, aside h3, aside h4, aside h5, aside h6, nav h1, nav h2, nav h3, nav h4, nav h5, nav h6
Использование CSS-селектора :has() на примерах
Итак, :has() — это CSS-псевдокласс родительского селектора. Другими словами, с помощью :has() можно изменить родительский элемент, содержащий определённый дочерний элемент либо элемент, следующий за ним.
CSS псевдокласс :has() отображает элемент в том случае, если любой из селекторов, переданный в качестве параметра (относительно :scope ), соответствует хотя бы одному элементу.
Всё ещё не очень понятно, правда? Давайте обратимся к практическим примерам.
Как использовать CSS-селектор :has()?
Рассмотрим следующий HTML-код с двумя родственными элементами с классом everybody . Как бы вы выбрали тот, у которого есть потомок с классом a-good-time ?
С CSS-селектором :has() это можно реализовать следующим образом:
Это выбирает первый экземпляр .everybody и применяет к нему animation . В этом примере целью является элемент с классом everybody . Условием является наличие потомка с классом a-good-time .
Но :has() гораздо больше возможностей. Вот некоторые из них.
Выбрать anchor , которые не имеют прямого потомка SVG:
Выбрать label , у которых есть родственный input :
Выбрать documentElement , в котором некое состояние присутствует в DOM:
:root:has(.menu-toggle[aria-pressed=”true”])
Совместимость :has() с браузерами
Не стоит забывать и о совместимости. Поскольку данный псевдокласс является нововведением, его, к сожалению, поддерживают не все браузеры:
CSS-селектор :has() на практике
Это реализация без единой строчки на JavaScript. Для начала можете посмотреть и самостоятельно протестировать пример, который реализован в CodePen:
Теперь рассмотрим его подробнее. Итак, CSS-псевдокласс :hover срабатывает, когда пользователь наводит на элемент мышью, но при этом активировать его необязательно.
Красивая плавность в примере заключается в создании набора кастомных свойств на основе сглаживающей кривой (easing curve). В нашем случае это:
Затем, чтобы применить это, нам нужны правила, которые обновляют пользовательское свойство —lerp для :hover или :focus для каждого элемента или блока. Код ниже предназначен для выбора пяти блоков с комбинацией родственных комбинаторов (+) и :has() .
:is(.block:hover, .block:focus-visible) < --lerp: var(--lerp-0); z-index: 5; >.block:has( + :is(.block:hover, .block:focus-visible)), :is(.block:hover, .block:focus-visible) + .block < --lerp: var(--lerp-1); z-index: 4; >.block:has( + .block + :is(.block:hover, .block:focus-visible)), :is(.block:hover, .block:focus-visible) + .block + .block < --lerp: var(--lerp-2); z-index: 3; >.block:has( + .block + .block + :is(.block:hover, .block:focus-visible)), :is(.block:hover, .block:focus-visible) + .block + .block + .block < --lerp: var(--lerp-3); z-index: 2; >.block:has( + .block + .block + .block + :is(.block:hover, .block:focus-visible)), :is(.block:hover, .block:focus-visible) + .block + .block + .block + .block
Последнее, что нужно сделать, это применить всё к самим блокам. Поскольку блоки выложены с помощью flexbox, можно использовать значение —lerp , чтобы изменить flex каждого блока, и translation для каждого элемента:
А вот сами значения —lerp сгенерированы с помощью утилиты GSAP для распределения значений с помощью сглаживающей кривой.