Is the CSS :Not() Selector Supposed to Work With Distant Descendants

Is the CSS :not() selector supposed to work with distant descendants?

Is this supposed to work like I think it should?

No, the behavior you're seeing is correct.

In your last example, although the <blockquote> contains a <p>, it's the <blockquote> itself that's matching *:not(p), as well as the condition that it must be a descendant of the <div>, which it is. The style is applied only to the <blockquote>, but it is then inherited by the <p> inside it.

The <p> element itself still counts against the negation, so the <p> itself is still being excluded from your selector. It's just inheriting the text color from its parent, the <blockquote> element.

Even if none of its relatively close ancestors matched the selector, you have elements like html and body to worry about as well — although you could probably just tack on a body selector in the very beginning:

body div...

This is why I often strongly advise against using the :not() selector for filtering descendants, especially when not qualified with a type selector (like div in your example). It doesn't work the way most people expect it to, and the use of inherited properties like color only serves to compound the problem, on top of making it even more confusing for authors. See my answers to these other questions for more examples:

  • Why doesn't this CSS :not() declaration filter down?
  • CSS negation pseudo-class :not() for parent/ancestor elements

The solution to the problem described is to simply apply a different color to <p> elements. You won't be able to simply exclude them with a selector because of inheritance:

/* Apply to div and let all its descendants inherit */
div {
color: red;
}

/* Remove it from div p */
div p {
color: black;
}

On Selectors Level 4: yes, :not() has indeed been enhanced to accept full complex selectors that contain combinators. Essentially, this means (once browsers begin implementing it) you will be able to write the following selector and have it do exactly what you want:

p:not(div p) {
color: red;
}

In case anyone is interested, this works in jQuery today.

CSS :not() selector on all descendants

In your example, the :not() selector is applied to the a element. This would only select a tags that did not have a .mystyle class on it.

#content > * > *:not(.mystyle) a {
color: green;
}

The above will select any descendants 2 levels down that don't have a .mystyle class, then colour all their decendant a tags green.

Below is a working example:

#content > div > div:not(.mystyle) a {  color: green;}
<div id="content">  <div id="one">    <div><p>This is a <a href="">link</a>.</p></div> <!-- should be green -->    <div><p>This is a <a href="">link</a>.</p></div> <!-- should be green --></div>  <div id="two">    <div class="mystyle"><p>This is a <a href="">link</a>.</p></div> <!-- should NOT be green -->    <div><p>This is a <a href="">link</a>.</p></div> <!-- should be green --></div></div>

CSS :not selector behavior

You need to use the > operator. This gets the immediate children of the element preceding. This will then get the ul immediately descending from #include. Updated:

JSFiddle

Updated code:

#include > ul {
color: blue !important;
}

You would not be able to to implicitly set styles by inheritance. They don't exclude ancestors because they don't trickle down. You will need to add new rules for other elements like so:

#include ul {
color: blue;
}

#exclude ul {
color: black;
}

Fiddle: Here

How to use the :not() selector?

Your :not() selectors actually work. The problem is your selectors are structured to satisfy multiple scenarios.

Here's an example:

.table-vertical :not(.picker) tbody

tbody is a descendant of .picker, so it is excluded.

But, tbody is also a descendant of .table-vertical, so it is included.

This is the reason your :not() selectors aren't applying. They are overridden by the selectors that do apply.

Try this approach:

  • There are two table elements in your HTML structure.
  • One is a child of .table-vertical
  • The other is a child of .picker
  • So, target only the child of .picker for exclusion.

.table-vertical,.table-vertical :not(.picker) > table {  display: block;  background: aquamarine !important;}
.table-vertical { padding: 5px;}
.table { border: 2px dashed red; width: 300px; height: 200px;}
.picker { width: 200px; height: 100px;}
.picker,.picker * { background-color: orange;}
<div class="table-vertical">  <table class="table">    <thead>      <tr>        <th>primary table</th>      </tr>    </thead>    <tbody>      <tr>        <td>          <div class="picker">            <table>              <thead>                <tr>                  <th>picker table</th>                </tr>              </thead>              <tbody>                <tr>                  <td></td>                </tr>              </tbody>            </table>          </div>        </td>      </tr>    </tbody>  </table></div>

Why this :not() instruction isn't working?

I guess css selectors are using an OR or ORELSE relation. So when you use a selector like :not(.foo) li something similar happens:

statement1: In first place it will check: is actual element's tagname is li? (true)

statement2_1:In second place it will check: is closest parents class not .foo? (true)

statement2_2:In next place it will check next parent: is next parent class not .foo (false)

Theoretically the following statement evaluates:

statement1 AND (statement2_1 ORELSE statement2_2)

true AND (true ORELSE false) => true AND true = > true

What if you just try other way:

li{  background-color: red;}
.foo li{ background-color: initial;}
<ul>  <li>11</li></ul><div class=foo>  <ul>    <li>22</li>  </ul></div>

Css selector not, not working for anchors

The :not(selector) selector matches every element that is NOT the specified element/selector.
The :not() CSS pseudo-class represents elements that do not match a list of selectors. Since it prevents specific items from being selected, it is known as the negation pseudo-class.

So to use it you should specify the css of the element that is then use not for those how don't

*:not(a){   color:blue;}* {  color:black;}
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p><span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</span><i>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</i><h1>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</h1><a>This is the link "a tag"</a>

Cascading with CSS :not pseudo-class

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:

  • CSS negation pseudo-class :not() for parent/ancestor elements
  • Why doesn't this CSS :not() declaration filter down?
  • Is the CSS :not() selector supposed to work with distant descendants?

CSS selector for something not inside something else

:not(.not-inside-this) and *:not(.not-inside-this) with the * are equivalent; in the case of the former, the universal selector is implied. See the spec.

It is currently not possible to construct a CSS selector that matches elements that are not descendants of specific elements for the reasons given in the following questions:

  • CSS negation pseudo-class :not() for parent/ancestor elements
  • Is the CSS :not() selector supposed to work with distant descendants?

The selector

.select-inside-this :not(.not-inside-this) .select-this

matches .select-this elements that are descendants of some element that is not .not-inside-this, which in turn is a descendant of .select-inside-this. It does not match .select-this elements that are not descendants of .not-inside-this within .select-inside-this.

This means, first off, that your selector will incorrectly match the following:

<div class="select-inside-this">
<div class="bar">
<div class="not-inside-this">
<div class="select-this"></div>
</div>
</div>
</div>

... because one of the ancestors of .select-this, .bar, is :not(.not-inside-this).

Additionally, this implies at least three levels of nesting (though it could be more). In your example, there are no other elements between .two.select-this and its containing .select-inside-this, so it will never match that element. This is why James Donnelly suggests adding .select-inside-this > .select-this to account for that particular case.

However it is still not possible to write a single complex selector using descendant combinators to match elements without a specific ancestor. The only way is to repeat the child combinator method with as many :not(.not-inside-this) as necessary, but this requires that you account for all possible cases. If you can't do that, then you're out of luck with CSS selectors.

Why doesn't this CSS :not() declaration filter down?

  1. Both of the span elements' parent div elements don't have the class no, regardless of whether any other ancestors do have it or not:

    <div> <!-- This is div:not(.no), pretty much a given -->
    <span>yes 1</span>
    </div>
    <div class="no"> <!-- In this case, although this is div.no... -->
    <div> <!-- ... this is div:not(.no)! -->
    <span>no 2</span>
    </div>
    </div>
  2. Both html and body, which are ancestors of your div and span elements, satisfy *:not(.no) when using a universal selector (or rather, when omitting a type selector). This causes all of your span elements to have the background color.

One solution to this is to anchor your negation filter to the body element using the child combinator, if your top-level div elements will always be children of body:

body > div:not(.no) span { background-color: #00f; }

jsFiddle demo

Another solution is to simply use override styles.



Related Topics



Leave a reply



Submit