Css Negation Pseudo-Class :Not() For Parent/Ancestor Elements

CSS negation pseudo-class :not() for parent/ancestor elements

Doesn't this read, "Select all h1 elements that have an ancestor that is not a div element...?"

It does. But in a typical HTML document, every h1 has at least two ancestors that are not div elements — and those ancestors are none other than body and html.

This is the problem with trying to filter ancestors using :not(): it just doesn't work reliably, especially when the :not() is not being qualified by some other selector such as a type selector or a class selector, e.g. .foo:not(div). You'll have a much easier time simply applying styles to all h1 elements and overriding them with div h1.

In Selectors 4, :not() has been enhanced to accept full complex selectors containing combinators, including the descendant combinator. Whether this will be implemented in the fast profile (and thus CSS) remains to be tested and confirmed, but once it is implemented, then you will be able to use it to exclude elements with certain ancestors. Due to how selectors work, the negation has to be done on the element itself and not the ancestor in order to work reliably, and therefore the syntax will look a little different:

h1:not(div h1) { color: #900; }

Anyone who's familiar with jQuery will quickly point out that this selector works in jQuery today. This is one of a number of disparities between Selector 3's :not() and jQuery's :not(), which Selectors 4 seeks to rectify.

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 :not on the parent does not work for descendant selectors but it does for child selectors, unless you explicitly specify the parent element type

:not(#outer) .c1 means: select all elements with class c1 and which are descendants of an element which doesn't have the ID outer.

This matches your first element, because it has class c1, and is a descendant of body, which doesn't have the ID outer.

Instead, div:not(#outer) .c1 means: select all elements with class c1 and which are descendants of a div element which doesn't have the ID outer. This doesn't match the second element, because all its ancestors either aren't div elements or have the ID outer.

Fianlly, :not(#outer) > .c1 means: select all elements with class c1 and which are a child of an element which doesn't have the ID outer. This doesn't match the third element, because its parent has the ID outer.

Can I apply CSS only if an ancestor does not contain a certain class?

Your interpretation is totally correct. You are applying a red background to any <div> that is not of class classToBeAvoid. Unfortunately this also applies to child <div>s, which is the reason for your first <div> to also be red (in fact your first parent <div> isn't red, but its child).

There are several ways to solve this issue (at least with some trade-offs).


1. The general siblings selector ~

You can use the general siblings selector, which will work in your case, because your .classToBeAvoid is before the following <div> elements.

div~:not(.classToBeAvoid)

div~:not(.classToBeAvoid) {  background-color: red;}
<div class="classToBeAvoid">  <div>    <p>      Shouldn't be a red background on any element around here.    </p>  </div></div>

<div> <p> Should be a red background </p></div>

Does the :not() pseudo-class support parent elements?

In CSS, can you target an element, then exclude instances of it based on parent wrapper elements higher up the DOM?

No, not the way you seem to be attempting with:

li:not(.wrap li)

You could, though, use instead a more simple approach of styling all <li> elements, and then specifically styling those elements that are descendants of an ancestor with the wrap class-name:

li {
/* generic styles for <li> elements */
}
.wrap li {
/* specific styles for <li> elements that are
descendants of elements with a class of '.wrap' */
}

li {  color: blue;}.wrap li {  color: red;  }
<div class="wrap">    <ul>         <li>link</li>         <li>link</li>         <li>link</li>    </ul></div><ul>     <li>link</li>     <li>link</li>     <li>link</li></ul>

CSS negation pseudo-class :not() for parent/ancestor elements

Doesn't this read, "Select all h1 elements that have an ancestor that is not a div element...?"

It does. But in a typical HTML document, every h1 has at least two ancestors that are not div elements — and those ancestors are none other than body and html.

This is the problem with trying to filter ancestors using :not(): it just doesn't work reliably, especially when the :not() is not being qualified by some other selector such as a type selector or a class selector, e.g. .foo:not(div). You'll have a much easier time simply applying styles to all h1 elements and overriding them with div h1.

In Selectors 4, :not() has been enhanced to accept full complex selectors containing combinators, including the descendant combinator. Whether this will be implemented in the fast profile (and thus CSS) remains to be tested and confirmed, but once it is implemented, then you will be able to use it to exclude elements with certain ancestors. Due to how selectors work, the negation has to be done on the element itself and not the ancestor in order to work reliably, and therefore the syntax will look a little different:

h1:not(div h1) { color: #900; }

Anyone who's familiar with jQuery will quickly point out that this selector works in jQuery today. This is one of a number of disparities between Selector 3's :not() and jQuery's :not(), which Selectors 4 seeks to rectify.

Why doesn't the :not selector in CSS work without a specified element?

:not(.dark-mode) will match anything that isn't .dark-mode, including <html> and <body>.

So this

:not(.dark-mode) .dark-mode-only {
display: none;
}

resolves to body .dark-mode-only and hides your text.

Description

There are several unusual effects and outcomes when using :not() that you should keep in mind when using it:

  • :not(.foo) will match anything that isn't .foo, including and .

https://developer.mozilla.org/en-US/docs/Web/CSS/:not

:not css selector is not working as expected

:not([dir="rtl"]) will effect any element with no [dir="rtl"], means even if the body have it, if any of the elements inside will not have it it still will apply to the child li. Even if the element extend the style, it will apply to it, since its not have the attribute dir with the value rtl.

When you add the body selector, the css will apply only when the body not have this attribute, but if it have, the fact that other elements not have it will not effect.



Related Topics



Leave a reply



Submit