Jerome Dahdah Jerome Dahdah -4 years ago 99
CSS Question

Cascading with CSS :not pseudo-class

I'm confused as to why this example doesn't work:

CSS:

p {
color: red;
}

div:not(.exclude) p {
color: green;
}


HTML:

<div>
<div class="exclude">
<p>I should be red</p>
</div>
<div>
<p>I should be green</p>
</div>
</div>


The end result is that both
<p>
are green, but I would have expected the first one to be red. Here's a JSFiddle.

Interestingly, I found three different ways to make it work:


  1. Remove the top-level
    <div>
    from the HTML

  2. Change the top-level
    <div>
    to a different element (e.g.
    <section>
    )

  3. Add an extra
    div
    to the beginning of the second CSS selector (
    div div:not(.exclude) p
    )



And another weird way to break it:


  1. Using solution 2 as a basis, wrap another
    <div>
    around the
    <section>



According to MDN:


This selector only applies to one element; you cannot use it to exclude all ancestors. For instance,
body :not(table) a
will still apply to links inside of a table, since
<tr>
will match with the
:not()
part of the selector.


That makes sense, but I don't think that this is happening here. Since there is nothing between
<div class="exclude">
and its direct child
<p>
, it should trigger the rule regardless of what it is nested inside. What am I missing? I'd really appreciate if someone could help me understand this.

Answer Source

Since there is nothing between <div class="exclude"> and its direct child <p>, it should trigger the rule regardless of what it is nested inside. What am I missing?

The <p> is a descendant of both the top-level <div> and <div class="exclude">. So while the latter doesn't match the selector, the former does, and therefore you have a match. It doesn't matter either that the ancestor that fails to match the selector is closer to the <p> than the one that does.

Solutions 1 and 2 work by eliminating that match altogether.

Solution 3 works when no other <div>s exist in the <p>'s ancestry, because then you restrict your selector to those exact criteria, in that exact order. Which means if you swapped the class attribute from the inner <div> to the outer one, it would no longer match the selector, and conversely if you swapped the class selector from the inner div to the outer one, the selector would not match the original HTML structure (again, assuming no other <div>s exist in the hierarchy).

Wrapping another <div> around the <section> just causes the selector to be matched again by that <div>. The <section> is ignored, in much the same way as <div class="exclude">.

See also:

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download