CSS: Not(), Selectors and Selecting Descendants

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>

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 Select all descendants but not children

You can combine the child selector with the descendant selector for that.

First of you want to select any element that is a child of parent by using .parent > *. Then you want to add the descendant selector to select the specific elementtype (div in your case).

.parent > * div {
color: red;
}
<div class="parent">

<div>NOT THIS
<div>THIS</div>
</div>

<div>NOT THIS</div>
<div>NOT THIS</div>

<div>NOT THIS
<div>THIS</div>
<div>THIS
<div>THIS</div>
</div>
</div>

</div>

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.

Apply CSS to any element except for certain class descendants?

You will not be able to exclude descendants this way without writing a separate selector. You won't be able to do this even using :not(), for the reasons stated here.

Fortunately, the fact that .light elements will only ever occur within the context of a .dark element and not vice versa makes this a little easier. Since you have a body CSS rule already, just add .light p to that rule, and move the entire ruleset underneath .dark p so .light p will take precedence:

.dark p {
color: #000;
}

body, .light p {
color: #ccc;
}

Updated fiddle

Alternatively if you want to keep the body rule on top, you could bump up the specificity of .light p to ensure it will take precedence:

body, body .light p {
color: #ccc;
}

.dark p {
color: #000;
}

CSS select direct children, but not if inside another nested child

Use div.parent > p.p

> is the child combinator. It matches only those elements matched by the second selector that are the direct children of elements matched by the first.

div.parent > p.p {color:green;}
<div class="parent">    <div class="ignore-me">        <p class="p">don't select me</p>        <p class="p">don't select me</p>        <p class="p">don't select me</p>        <!-- I don't know how many <p> gonna be here  -->    </div>    <p class="p">select me</p>    <p class="p">select me too</p></div>


Related Topics



Leave a reply



Submit