:has()

The functional :has() CSS pseudo-class represents an element if any of the relative selectors that are passed as an argument match at least one element when anchored against this element. This pseudo-class presents a way of selecting a parent element or a previous sibling element with respect to a reference element by taking a forgiving relative selector list as an argument.

/* 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; }

The :has() pseudo-class takes on the specificity of the most specific selector in its arguments the same way as :is() and :not() do.

Syntax

:has( <forgiving-relative-selector-list> )

The relative selector list parameter is forgiving. Normally in CSS, when a selector in a selector list is invalid, then the whole list is deemed invalid. When a selector in a :has() selector list fails to parse, the incorrect or unsupported selector will be ignored and the others will be used.

Note that if the :has() pseudo-class itself is not supported in a browser, the entire selector block will fail (unless :has() itself is in a forgiving selector list, such as in :is() and :where()).

The :has() pseudo-class cannot be nested within another :has(). Also, pseudo-elements are not valid selectors within :has(). This is because many pseudo-elements exist conditionally based on the styling of their ancestors and allowing these to be queried by :has() can introduce cyclic querying. While :has() and pseudo-elements are not valid :has() selectors, as the selector list is forgiving, they will just be ignored. The selector will not fail.

Examples

With the sibling combinator

The :has() style declaration in the following example adjusts the spacing after H1 headings if they are immediately followed by an H2 heading.

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

h1, h2 {
  margin: 0 0 1.0rem 0;
}

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

Result

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

This example builds on the previous example to show how to select multiple elements with :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

h1, h2, h3 {
  margin: 0 0 1.0rem 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 */
}

Specifications

Specification
Selectors Level 4
# relational

Browser compatibility

BCD tables only load in the browser

See also