1

I want the following selector to match only those <span> elements that are direct children of '.grand-grand-child' and not descendants of '.grand-grand-parent':

:not(.grand-grand-parent) .grand-grand-child > span { 
  color: blue; 
}

But it fails to apply the rule. Is it possible to solve the problem without Javascript? In my experience, :not rules at the beginning have to be followed with direct path made with > signs. Am I right?

raina77ow
  • 103,633
  • 15
  • 192
  • 229
azrael
  • 263
  • 3
  • 13
  • 1
    They don’t *have* to be, but this probably won’t do what you want (if what you want is `.grand-grand-child:not(.some-grand-grand-parent *)`). Normally you’d do it by applying styles to `.grand-grand-child > span` and reversing them in `.some-grand-grand-parent .grand-grand-child > span`. – Ry- Jan 11 '18 at 20:03
  • `:not(.some-grand-grand-parent *)` is not supported by Chrome (at least at the moment), this selector is just ignored. – raina77ow Jan 11 '18 at 20:21

1 Answers1

2

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>

And the answer is blue, even though that .child element is clearly matched by .parent .child rule. The problem is, this rule reads as

match any element with class 'child' if one of its ancestors is without class 'parent'

And of course, it has such an ancestor - <body> element. Now compare with this fragment:

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

And now the answer is black, as the selector reads as...

match any element with class 'child' if its direct parent is without class 'parent'

Another way will be opened when browsers start supporting CSS Selectors Level 4 negation spec, allowing something more than simple selector as :not argument. It'll be possible to write something like:

.child:not(.parent *) { /* */ }

And now if any element is ancestor chain of .child matches .parent, it's not matched. But both Chrome and Firefox at the moment of writing still lack support of this feature - they only support CSS Level 3 negation.

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • I just want descendant to be red because there is NOT grand parent in second rule but the browser matches it. – azrael Jan 11 '18 at 20:20
  • I run the code snippet you have provided and descendand is blue. It shoud be matched by the first rule and not the second one. – azrael Jan 11 '18 at 20:26
  • I asked about it because I wanted to now if it is possible to solve this problem with CSS. And I don't understand why some people thing that this is a bad question. – azrael Jan 11 '18 at 20:27
  • You are right, I have to wait for CSS4 and hack it with Javascript now. – azrael Jan 11 '18 at 20:35
  • Rewrote the answer to clarify the explanation a bit. Yes, as it stands you either rewrite the rules or cover the distance with JS. – raina77ow Jan 11 '18 at 20:39