Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How does the :has() selector choose its matching elements?

Quick answer

:has() matches an element when a selector inside its parentheses matches in the relationship described from that element. For example, article:has(img) selects an article with a matching descendant image, while article:has(> img) requires a direct child. Combinators such as + can describe sibling relationships. To debug an unexpected match, identify the candidate element, its anchor, and the relationship each selector requires. [S1][S2]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I see :has() in CSS, but what is it actually checking? How does it decide which element to select?

Geez

:has() lets a selector choose an element based on whether another selector matches in relation to it. Think of the element before :has() as the anchor: CSS considers a candidate element, then checks whether the condition inside the parentheses matches relative to that candidate. [S1]

For example, article:has(img) selects an article when it has a matching img descendant. The img need not be an immediate child. [S1][S2]

<article id="with-image">
  <div><img src="example.png" alt="Example"></div>
</article>
<article id="without-image">
  <p>Text only</p>
</article>
article:has(img) {
  outline: 2px solid teal;
}

Here, #with-image matches because an image occurs below it in the document tree; #without-image does not. The selector checks the relationship, not the image’s visual position.

Follow-up question

How do I make the condition require a direct child instead of any descendant?

Geez

Put the child combinator > at the start of the relative selector: article:has(> img). That says the matching image must be a direct child of the anchored article. With no explicit leading combinator, a relative selector has an implied descendant relationship instead. [S2]

<article id="direct">
  <img src="direct.png" alt="Direct child">
</article>
<article id="nested">
  <div><img src="nested.png" alt="Nested image"></div>
</article>
article:has(> img) {
  border: 2px solid green;
}

The first article matches; the second does not. Its image is a descendant, but the div sits between the article and image. If you meant “anywhere inside,” use article:has(img) instead. [S1][S2]

Follow-up question

Can :has() check a nearby sibling too? I want to style a heading when a certain section follows it.

Geez

Yes. A relative selector inside :has() can describe a sibling relationship, not only descendants. The adjacent-sibling combinator + checks the immediately following sibling. [S1][S2]

<h2 class="section-title">References</h2>
<div id="topic">
  <p id="reference">Reference text</p>
</div>
h2.section-title:has(+ div#topic > #reference) {
  color: teal;
}

Read the condition from the heading anchor: its next sibling must be a div with id="topic", and that div must have a direct child with id="reference". This relationship follows the DOM structure, not merely what appears visually beside the heading. The example uses the sibling and child relationships illustrated in the selector reference. [S2]

Follow-up question

My selector matches more elements than I expected. What should I inspect, and can you give me a reusable way to read it?

Geez

Start with the element named before :has(); that is the kind of element being selected. Then read the contents of :has() from that anchor outward, checking each combinator. A missing leading > means the relative selector uses an implied descendant relationship, which is a common reason a nested match qualifies unexpectedly. [S2]

For example, if section:has(.notice) matches a section containing a notice several levels down, that follows the descendant relationship. If only a direct child should count, use section:has(> .notice). If the condition involves a sibling, check whether + means the immediately adjacent sibling you intended. [S1][S2]

Also inspect the selector list and syntax. The selector reference notes that an invalid selector in a non-forgiving selector list can invalidate the list, so a syntax problem can affect matching too. [S2] The examples here explain selector relationships; they do not establish browser support for any particular environment.

A reusable reading template:

Candidate: [element before :has()]
Anchor: each candidate element of that type
Required relationship: [descendant / direct child / adjacent sibling]
Condition: [selector that must match]
Expected matching candidates: [IDs or description]

Work through the DOM one candidate at a time. Ask, “Starting from this candidate, can I follow the specified relationship to an element matching the condition?” If yes, it matches; if not, it does not.

A practical checklist

  • Identify the element type or selector before :has(); these are the candidates being tested.

  • Treat each candidate as the anchor, then trace the relationship inside the parentheses from that anchor.

  • Check whether the condition means any descendant, a direct child (>), or a sibling such as the next adjacent sibling (+).

  • Look for an omitted leading combinator: without one, the relative selector implies a descendant relationship.

  • Check every selector in a selector list for syntax errors, which can affect a non-forgiving list.

  • Compare the expected matches with the actual DOM structure, not only the visual layout.

A few more useful answers

Does `:has(img)` mean the image must be a direct child?

No. Without a leading combinator, the relative selector implies a descendant relationship, so nested images can qualify. Use :has(> img) when the image must be a direct child. [S2]

Can `:has()` test a sibling instead of a descendant?

Yes. A relative selector can describe sibling relationships as well as descendants. For example, + describes an adjacent-sibling relationship. [S1][S2]

What does this explanation establish about browser support?

The cited extracts explain how relative matching and relationships work; they do not establish browser support for a specific browser, version, or project environment.

Sources

References used for this guide.

  1. [S1] Pseudo-classes — CSS reference — MDN
    developer.mozilla.org · Checked
  2. [S2] CSS selector structure — MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work