1

I'm struggling to get the following code to target correctly. Can someone help me resolve and finish this issue?

I have a star rating set of inline radio buttons. It's annoying but each radio field has to wrapped in a <div class="option">. My target behaviour is:

1) When a radio button is selected (i.e value number 4), all previous labels are targeted and appropriately colored.

2) When hovering, the css overrides the checked behaviour and styles all labels before the input being hovered on.

I had it working without each input being wrapped in the .option tag however, as it is a requirement I can't get it to work with the option tag.

--

So, I've simplified the code.

Where this would work correctly....

label {
  font-size: 20px;
}
input[type*="radio"]:checked + label {
  color: blue;
}

input[type*="radio"]:checked + label ~ input[type*="radio"] + label {
  color: red;
}
<div>
  <input type="radio" id="q1" name="radio" ><label for="q1">1</label>
  <input type="radio" id="q2" name="radio" ><label for="q2">2</label>
  <input type="radio" id="q3" name="radio" checked><label for="q3">3</label>
  <input type="radio" id="q4" name="radio"><label for="q4">4</label>
  <input type="radio" id="q5" name="radio"><label for="q5">5</label>
</div>

How do I make the following work with the exact same behaviour as above???

label {
  font-size: 20px;
}
.option {
  display: inline-block;
}
input[type*="radio"]:checked + label {
  color: blue;
}

.option > input[type*="radio"]:checked + label ~ .option > input[type*="radio"] + label {
  color: red;
}
<div class="container">
  <div class="option">
    <input type="radio" id="q1" name="radio"><label for="q1">1</label>
  </div>
  <div class="option">
    <input type="radio" id="q2" name="radio"><label for="q2">2</label>
  </div>
  <div class="option">
    <input type="radio" id="q3" name="radio" checked><label for="q3">3</label>
  </div>
  <div class="option">
    <input type="radio" id="q4" name="radio"><label for="q4">4</label>
  </div>
  <div class="option">
    <input type="radio" id="q5" name="radio"><label for="q5">5</label>
  </div>
</div>
Luke Cottingham
  • 505
  • 1
  • 4
  • 22
  • 1
    Consider creating a [**stack snippet**](https://stackoverflow.blog/2014/09/16/introducing-runnable-javascript-css-and-html-code-snippets/) that reproduces the problem. If there's any code that isn't part of the problem, remove it. – Michael Benjamin Sep 24 '17 at 13:01
  • 1
    To be honest, I think it's the `~` selectors not able to move up the list because of the `.option` classes. Not sure how to make the `~` selector move past that class – Luke Cottingham Sep 24 '17 at 13:27
  • @Michael_B thanks for your suggestion... I have simplified the code and included them in snippets. Maybe this will help you see my problem – Luke Cottingham Sep 24 '17 at 14:09
  • You're saying you want to target all previous elements. In your example that works, you're targeting all following elements. – Michael Benjamin Sep 24 '17 at 14:13
  • Also, keep in mind that a `div` cannot be a child of a `ul`. That's invalid HTML. Swap the `li` and `div` elements. – Michael Benjamin Sep 24 '17 at 14:14
  • ... which would trivially resolve your main problem as well, since you would then be able to target the sibling li followed by its child div, assuming the class attribute stays with the li element. – BoltClock Sep 24 '17 at 14:22
  • 1
    The `~` is a _sibling_ selector, it can't match parents' siblings. So you are correct in your assumption, it can't go 'up' the node tree and then find a sibling – Matheus Avellar Sep 24 '17 at 14:30
  • @MatheusAvellar @boltclock @Michael_B thanks for your responses. I've just updated my question - apologies I was writing the post whilst I was on the move and didn't apply my question to input radio fields. The question snippets have been updated... do you have any wisdom on targeting the sibling selectors with this`option` divs wrapping each `input`? – Luke Cottingham Sep 24 '17 at 17:39
  • Then yes, your comment from earlier and Matheus's comment are right. – BoltClock Sep 24 '17 at 17:43
  • @BoltClock is there a method I'm not aware of where I can use CSS to select what I want to? OR - am I looking at a javascript hack for this? – Luke Cottingham Sep 24 '17 at 17:50
  • Perhaps you can avoid the need for the `.option` wrapper? Show us the style you're trying to achieve (ie why you're using the wrapper) and maybe there's a way to make do without it. – Moob Sep 24 '17 at 18:03

2 Answers2

2

As of the current state of CSS, this cannot be done without the aid of JavaScript.

Will it ever be possible? Well, yeah.


Pseudo-class :has()

The Selectors Level 4 Editor's Draft (from August 23 2017) includes a :has() pseudo-class which would solve this issue.

Something along the lines of...

.option:has(input:checked) ~ .option > label {
    color: red;
}

...should select labels coming after a selected option.

As per this great answer:

this is still not supported by any browser.


As BoltClock has beautifully mentioned in the comments, the 'subject' selector was dropped (as can be seen from this awesome answer).

For the sake of completion, though, I'll keep some of my original answer:

On the same draft (not the same!) they introduced the 'subject' of the selector ($) which determines which part of the selector to apply the propeties to. See this other great answer. However, that has now been dropped.

Matheus Avellar
  • 1,411
  • 1
  • 22
  • 29
  • 1
    That's the same spec, but not the same draft. The one that contains the :has() pseudo is the *editor's* draft, the current version. The one that contains the subject indicator is the September 2011 FPWD that's almost **six years old** now. [The :has() pseudo *replaced* the subject indicator in 2015.](https://stackoverflow.com/questions/27982922/latest-on-css-parent-selector/27983098#27983098) – BoltClock Sep 24 '17 at 18:08
  • That's all I needed to know I suppose... javascript hacks to come! Thank you for your great answer – Luke Cottingham Sep 24 '17 at 18:16
  • Thank you and especially @BoltClock! I have learned a new thing today! – Matheus Avellar Sep 24 '17 at 18:17
0

You just need to be less specific. DEMO.

Target the .option w/a nested li.b, next to any .option

.option ~ .option li.b {
  background-color: powderblue;
}
Ari
  • 1,335
  • 10
  • 16
  • Thanks @Ari, I made a mistake in my original question (sorry!). Do you have answers for the updated question and code snippets? – Luke Cottingham Sep 24 '17 at 17:40