Does :Not() Negation Accept Descendant Selectors

Does :not() negation accept descendant selectors?

In Selectors Level 3, the answer would be NO. The :not() notation accepts only simple selectors.

6.6.7. The negation
pseudo-class

The negation pseudo-class, :not(X), is a functional notation taking
a simple selector (excluding the negation pseudo-class itself) as an
argument. It represents an element that is not represented by its
argument.

What is a simple selector?

From selector syntax:

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

Nothing about a descendant selector.

HOWEVER, in Selectors Level 4, :not() accepts complex selectors, which would include descendant combinators. Browser support is still quite weak for this specification.

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 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.

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.

CSS: using negation on * all elements

It's working, but you're telling the browser that img elements do have to be wrapped in something. So img tags directly inside the article tag will not work, but anything nested e.g. in a div will:

article *:not(figure) img {   border: 2px solid red; }
<article>  <img src="//placehold.it/50">  <figure class="x">    <img src="//placehold.it/50">  </figure>  <div>    <img src="//placehold.it/50">  </div></article>

negation pseudoclass on CSS3 does not work on childs

It works fine if you simplify the selector:

.pag > :not(p){
color:blue;
}

JS Fiddle demo.

Albeit this 'works fine' only with the caveat that you have to specify a selector, with this approach, for every parent-child relationship; which may become burdensome.

I suspect that it's the simplicity that's required:

The negation pseudo-class, :not(X), is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument. It represents an element that is not represented by its argument.

A 'simple selector' is defined as:

either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

This seems to imply that any selector incorporating combinators (such as white-space, >, + or ~, among others) is not 'simple', unfortunately.

References:

  • Negation (:not()) pseudo-class.
  • Simple selector definition.

CSS :not() not working properly

Try this and there you go:

.header-container > a {
display:none;
}

Thanks!

Negative selecting without direct path in CSS

See, there's a problem here: the first part of this selector will be applied to any element in the second selector's match ancestor chain (in attempt to match the whole rule). Consider the following:

:not(.parent) .child {  color: blue;}
<div class="parent">  <div class="child">      Which color am I?  </div></div>

CSS :not selector is not applying correctly

In CSS 3, more specifically Selectors Level 3:

The negation pseudo-class, :not(X), is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument.

The key here being simple selector. :not only access simple selectors like .classsName, not things with a ,, or .className .child.

Note however that this has been updated in Selectors Level 4:

The negation pseudo-class, :not(), is a functional pseudo-class taking a selector list as an argument.

Note the change of simple selector to selector list. That means that what you are trying to do will be possible, but only in browsers implementing the new spec.

You can always apply the styles broadly and then revert them:

* {
color: red
}

.reset-this, .reset-this * {
color: black;
}

But that is pretty terrible and gross and ugly. As you commented, it would be much better to design your class structures to avoid this.



Related Topics



Leave a reply



Submit