CSS cascading: not a pseudo-class

I am confused why this example does not 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 expected the first to be red. Here's the JSFiddle.

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

  • Remove top level <div> from HTML
  • Change the top level of the <div> to another element (e.g. <section> )
  • 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 the basis, wrap another <div> around the <section>

According to MDN :

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

That makes sense, but I don't think this is happening here. Since there is nothing between <div class="exclude"> and its direct child <p> , it should run the rule no matter what it is nested inside. What am I missing? I would really appreciate it if someone would help me figure this out.

+8
dom html css css-selectors css3
source share
3 answers

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

<p> is a stream of both the top level <div> and <div class="exclude"> . That way, although the latter does not match the selector, the former does, and so you have a match. It doesn't matter that the ancestor that doesn't match the selector is closer to <p> than the one that does.

Solutions 1 and 2 work by eliminating this match.

Solution 3 works if there are no other <div> in the ancestor <p> , because then you restrict your selector to those exact criteria in that exact order. This means that if you changed the class attribute from the internal <div> to the external, it would no longer correspond to the selector, and, conversely, if you switched the class selector from the internal div to the external, the selector would not correspond to the original HTML structure (again if there are no other <div> in the hierarchy).

Wrapping the other <div> around the <section> simply causes the selector to be mapped to that <div> again. <section> ignored in much the same way as <div class="exclude"> .

See also:

  • CSS negation: not () alias for parent / ancestor elements
  • Why is this CSS: not () filter not removed?
  • Is the CSS: not () selector supposed to work with remote descendants?
+6
source share

You have an override of <div> even higher than <div class="exclude"> .

In your styles, you indicated that - with respect to the :not pseudo-class - any ancestor of the <div> . (i.e. grandparent <div> is as good as parent <div> .)

To focus on the immediate parent, your CSS should use a direct child selector ( > ):

 p { color: red; } div:not(.exclude) > p { color: green; } 
 <div> <div class="exclude"> <p>I should be red</p> </div> <div> <p>I should be green</p> </div> </div> 
+4
source share

You found a general error while using :not , as described in other answers. The classic CSS approach to solving this problem and how we did it before CSS3 provided us with the ability to shoot in the foot with :not , is to write a few rules, one of which relates to the general case, the second with an exception. In your case, it will look like this:

 p { color: red; } div p { color: green; } div.exclude p { color: red; } 
+1
source share

All Articles