2

I want .c to be red & bold ONLY IF it doesn't have a parent .b (direct or indirect):

.a :not(.b) .c {
  color: red;
  font-weight: bold;
}
<div class="a">
  <div class="not-b">
    <div class="c">bold red</div>
    <div class="b">
      <div class="c">normal black</div>
    </div>
  </div>
</div>
Kawd
  • 4,122
  • 10
  • 37
  • 68
  • Why are you looking for only one selector? This can easily be done with `.a > *:not(.b) > .c, .a > .c`. – James Whiteley Jun 01 '18 at 13:47
  • Even if I use more than one selectors, your solution is still wrong I'm afraid. Please read the OP carefully (and the title of the OP). You're targeting "direct" children so this will not do. You're are not covering all possible scenarios. – Kawd Jun 01 '18 at 13:52
  • Fair enough, it worked for your examples so I forgot to account for something like `.a.g.f.c`. – James Whiteley Jun 01 '18 at 14:00
  • Indeed, I'm looking for a universal solution that only deals with classes .a, .b and .c - but no worries, thanks for trying anyway! – Kawd Jun 01 '18 at 14:03
  • To the best of my knowledge its not possible because the `.c` satisfies the `*:not(.b)` selector, but then there is no child with the class of `c` to satisfy the `.c` selector. At least not possible with a single selector. – amflare Jun 01 '18 at 14:44

5 Answers5

3

One solution is to split the CSS definitions up. Start by assuming the text will always be red if .a is followed at some point by .c, then specify that you want unset it all if .b is in the way at any point.

.a .c {
  color: red;
  background-color: gray;
  display: inline-block;
  padding: 2em;
  animation-name: spin;
  animation-duration: 4000ms;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(-360deg);
  }
}

.a .b .c {
  all: unset;
}
<div class="a">
  <div class="g">
    <div class="c">i should be red and spinning</div>
  </div>
</div>

<div class="a">
  <div class="c">i should be red and spinning</div>
</div>

<div class="a">
  <div class="g">
    <div class="f">
      <div class="c">i should be red and spinning</div>
    </div>
  </div>
</div>

<div class="a">
  <div class="b">
    <div class="c">i should not be red or spinning</div>
  </div>
</div>

<div class="a">
  <div class="g">
    <div class="b">
      <div class="f">
        <div class="c">i should not be red or spinning</div>
      </div>
    </div>
  </div>
</div>

<div class="a">
  <div class="g">
    <div class="d">
      <div class="b">
        <div class="f">
          <div class="e">
            <div class="c">i should not be red or spinning</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Note: all doesn't work in IE. Also, the spin animation is taken from here - I just put that in to demonstrate how all overwrites everything including any animations.

James Whiteley
  • 3,363
  • 1
  • 19
  • 46
  • "specify that you want something different to happen if .b is in the way at any point": The "something different" that I would want in that case would be for `.a .c {....}` to be completely discarded, NOT overriden. Because in my real-life problem I won't really know what `.a .c {...}` contains so that I may then override every single property one by one inside `.a .b .c { /* override everything that .a .c. { ... } did */ }`. That is why I'm trying to go the other way round. I think it's a lost cause tbh. – Kawd Jun 01 '18 at 14:15
  • I see. I'll have a think about it but it isn't a problem I've ever encountered before at work - usually I know what is in `.a .c` and can get away with just overriding everything and that isn't an issue. – James Whiteley Jun 01 '18 at 14:20
  • please see my updated OP, I could give you the real context but I think my OP would become too verbose and confusing I'm afraid – Kawd Jun 01 '18 at 14:21
  • It's already pretty confusing as you've abstracted the problem too much for our solutions to be helpful. If you could attach a codepen or jsfiddle or something for us to go off, that would probably be more useful at this point. – James Whiteley Jun 01 '18 at 14:34
  • If you set the attributes you want to set in the `.a .c` block, you can essentially undo everything by setting those properties to `unset` in a `.a .b .c` block, nullifying everything you would otherwise have done. I'll update my answer to show this but it's the only concise solution I've been able to come up with as CSS isn't really a "coding language" and isn't designed to handle complex boolean things like in your question. – James Whiteley Jun 01 '18 at 14:41
  • The `all: unset;` would've been a pretty good solution but it's a shame that it's not widely supported yet. I will upvote. But a solution is yet to be found and has nothing to do with anyone's skills I think it's simply not possible with current state of browser-supported CSS. I will edit my OP and add the actual context where this weird need of mine came from :) – Kawd Jun 01 '18 at 15:01
  • 1
    `unset` is pretty widely supported - it is only not supported on IE. I suppose you could always implicitly `unset` everything you previously set in `.a .c` rather than using the `all` catchall. Hope I was some help at least! – James Whiteley Jun 01 '18 at 15:33
2

There is no single selector that will suffice here.

You just need to add .a > .c to your selector

.a *:not(.b) .c,
.a > .c {
  color: red;
}
<div class="a">
  <div class="g">
    <div class="c">i should be red</div>
  </div>
</div>

<div class="a">
  <div class="c">i should be red</div>
</div>
Paulie_D
  • 107,962
  • 13
  • 142
  • 161
0

I want .c to be red only if there is no parent .b between .a and .c or if there is no parent at all between .a and .c.

This statement can logically be reduced to:

I want .c to be red only if it is a child, not descendant, of .a

In which case,

.a > .c { color: red; }
Mitya
  • 33,629
  • 9
  • 60
  • 107
0

First, i think you should defaults to red, the c class. Then do the following:

CSS

.a .c {
    color: red
}

.a .not-red .c {
    color: blue;
}

HTML

<div class="a">
    <div class="b not-red">
        <div class="c">
            test
        </div>
    </div>
</div>

I´ve tested and it´s working as you want.

Nuno Bentes
  • 1,475
  • 13
  • 26
  • 1
    You are changing the constraints of the problem to fit your solution :-) (i.e. I don't want .c to default to red) – Kawd Jun 01 '18 at 13:54
  • Sorry, i didn´t understand the big picture of the problem... i guess now i~ve got your solution!! – Nuno Bentes Jun 01 '18 at 13:57
  • Your solution is specific to my random examples. It is not a universal one. Do not use `.g`. Only deal with a, b and c in your selector(s). – Kawd Jun 01 '18 at 14:01
  • Ok, you could use my final solution. You can attached the class to any element that wrapps the .c class and at any element depht – Nuno Bentes Jun 01 '18 at 14:08
  • in my opinion, it´s easier, if you add a simple class to the .c parent removing the color red. – Nuno Bentes Jun 01 '18 at 14:12
0

You need to select a rule for every level of nesting that you can go . There is no posibility to set a generic solution for any level

On the other hand, you shouldn't have a DOM with so many levels of nesting that you can't handle it with say 6 selectors ....

.a > .c,
.a > *:not(.b) > .c, 
.a > *:not(.b) > *:not(.b) > .c, 
.a > *:not(.b) > *:not(.b) > *:not(.b) >.c {
  background-color: red;
}
<div class="a">
  <div class="g">
    <div class="c">i should be red</div>
  </div>
</div>

<div class="a">
  <div class="c">i should be red</div>
</div>

<div class="a">
  <div class="g">
    <div class="f">
      <div class="c">i should be red</div>
    </div>
  </div>
</div>

<div class="a">
  <div class="b">
    <div class="c">i should not be red</div>
  </div>
</div>

<div class="a">
  <div class="g">
    <div class="b">
      <div class="f">
        <div class="c">i should not be red</div>
      </div>
    </div>
  </div>
</div>

<div class="a">
  <div class="g">
    <div class="d">
      <div class="b">
        <div class="f">
          <div class="e">
            <div class="c">i should not be red</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
VXp
  • 11,598
  • 6
  • 31
  • 46
vals
  • 61,425
  • 11
  • 89
  • 138
  • I can't know every possible level in advance so there's probably no solution to my problem I'm afraid. – Kawd Jun 01 '18 at 14:35
  • No, if you can not set a maximum number of nesting, there is no CSS solution. – vals Jun 01 '18 at 14:36
  • Saying that there is no other solution would justify a downvote only if you can show that a solution is available – vals Jun 01 '18 at 14:40
  • a no vote is justified if what is suggested does not solve the problem described in the op - a downvote was a bit harsh indeed, I cant seem to be able to take it back now :( – Kawd Jun 01 '18 at 22:26