Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

Why does a CSS selector match more elements than I expected?

Quick answer

Start with the selector’s rightmost part, then check each candidate element against the relationships to its left. A space allows a descendant at any depth; > requires a direct child. + selects the immediately following sibling, while ~ can select later siblings with the same parent. A comma separates alternatives, so an element need only match one branch. [S1] [S2]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

Why does .card p match a paragraph that isn’t directly inside the card? I’m reading the space as meaning “inside,” but I expected it to mean an immediate child.

Geez

The space means “descendant,” not “direct child”: the paragraph can be nested at any depth inside an element with class card. [S1] Try tracing this small tree:

<section class="card">
  <p>Direct paragraph</p>
  <div class="body">
    <p>Nested paragraph</p>
  </div>
</section>
<p>Outside paragraph</p>

For .card p, start at each p and ask whether it has a .card ancestor. Both paragraphs inside the section qualify, including the nested one. The outside paragraph has no such ancestor, so it does not qualify. If you want only direct child paragraphs, use .card > p; the > requires the p to be a direct child of .card. [S1]

Follow-up question

What if the selector is longer, or uses + and ~? I find it hard to know which elements to check first.

Geez

Work from right to left. First list elements matching the selector’s rightmost part, then check whether each candidate satisfies the relationship immediately to its left. MDN describes complex selectors as expressing simultaneous conditions and notes that they can be read this way. [S2]

For example, in .card > .body p, start with each p. It must have an ancestor with class body, and that .body element must be a direct child of .card. The space permits the paragraph to be deeper inside .body; the > restricts the relationship between .card and .body. [S1]

Sibling combinators use a different relationship. In h2 + p, the p must immediately follow an h2 and share its parent. In h2 ~ p, a matching p can follow the h2 later under the same parent; it need not be adjacent. [S1] Draw the relevant parent and sibling order, then check each candidate against the exact relationship.

Follow-up question

I also found .card p, .notice p. Does the comma make the rule stricter, or does it combine the two matches?

Geez

It combines alternatives: an element matches the comma-separated list if it matches at least one branch. The comma means “or,” not “and.” [S2]

Check .card p on its own, then .notice p on its own. In the example above, add <aside class="notice"><p>Notice paragraph</p></aside> outside the section. The paragraph in the section matches the first branch; the notice paragraph matches the second. Neither needs to satisfy both branches. [S2]

When debugging a list, write down the matches for each branch separately, then combine them. That makes it easier to spot whether the unexpected element qualifies through a different branch than the one you were examining.

Follow-up question

Can you give me a repeatable routine for checking my actual rule, rather than just this example?

Geez

Use a reduced HTML example that keeps the relevant nesting, classes, and sibling order. Then:

  1. Copy the selector exactly and list elements that could match its rightmost part.
  2. For each candidate, trace left. Check ancestors for a space, direct parenthood for >, and same-parent sibling order for + or ~. [S1]
  3. If the selector has commas, test every branch separately and combine the results as alternatives. [S2]
  4. Write down the intended and unexpected matches. For each unexpected element, note which branch and relationships allow it to qualify.
  5. Change one relationship at a time and trace the selector again against the reduced example.

For .card p, for instance, the nested paragraph qualifies because it has a .card ancestor. If only direct paragraphs are intended, compare that result with .card > p. The trace explains what the selector’s relationships allow; check the revised rule in the page where you use it.

A practical checklist

  • Start with elements matching the selector’s rightmost part.

  • Trace each combinator against the actual ancestor, parent, or sibling relationship.

  • Check comma-separated branches independently, then combine their matches as alternatives.

  • Record intended and unexpected matches before changing the selector.

  • Change one relationship at a time and trace again against a reduced HTML example.

A few more useful answers

Does a space in a selector mean direct child?

No. It allows a descendant at any depth. Use > when the matched element must be a direct child. [S1]

Does a comma require an element to match every listed selector?

No. A comma-separated selector list matches an element if it matches at least one branch. [S2]

What is the difference between `+` and `~`?

+ matches the immediately following sibling; ~ can match later siblings with the same parent, whether or not they are adjacent. [S1]

Sources

References used for this guide.

  1. [S1] CSS selectors and combinators — 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