I need to change the styles of 2 radio containers when the radio option has been clicked or hovered
.gchoice_1_15_0, .gchoice_1_15_1 {
width: 48%;
color: #58595B;
border: 1px solid #E4E4E4;
border-radius: 10px;
padding-left: 1em;
background: #fff;
transition: all ease-in-out .3s;
box-shadow: none;
}
//Yes option
<div class="gchoice gchoice_1_15_0">
<input class="gfield-choice-input" name="input_15" type="radio" value="Yes" id="choice_1_15_0" onchange="/*gformToggleRadioOther( this )*/">
<label for="choice_1_15_0" id="label_1_15_0">Yes</label>
</div>
//No option
<div class="gchoice gchoice_1_15_1">
<input class="gfield-choice-input" name="input_15" type="radio" value="No" id="choice_1_15_1" onchange="/*gformToggleRadioOther( this )*/">
<label for="choice_1_15_1" id="label_1_15_1">No</label>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Once any of the options are checked or hovered I need to change the styles of the correct container:
If the "Yes" option is checked, change .gchoice_1_15_0 to
background: #EF8B22;
border: 1px solid #EF8B22;
box-shadow: 0px 0px 6px 0px rgb(239 139 4 / 30%);
If the "No" option is checked, change .gchoice_1_15_1 to
background: #EF8B22;
border: 1px solid #EF8B22;
box-shadow: 0px 0px 6px 0px rgb(239 139 4 / 30%);
I tried with this CSS but didn't work:
#choice_1_15_0:checked ~ .gchoice_1_15_0 {
background: #EF8B22;
border: 1px solid #EF8B22;
box-shadow: 0px 0px 6px 0px rgb(239 139 4 / 30%);
}
How can I achieve that?
PD. HTML code can't be modified.
CodePudding user response:
The problem:
I tried with this CSS but didn't work:
It doesn't work because CSS selectors are "forward-only".
- The
~
and - Conversely: It can only select elements based on their ancestor and previous-sibling elements' attributes and DOM state.
- Indeed, the
~
doesn't mean "any sibling", it means "any future siblings".
- The
In your case: a CSS selector cannot use the
:checked
state of the<input id="choice_1_15_0" />
when selecting<div >
...- ...but CSS can use it to select
<label for="choice_1_15_0">
.
- ...but CSS can use it to select
CSS selectors work this way for multiple reasons, the main one thing performance: the algorithm for applying CSS rules with these restrictive forward-only rules is much simpler and faster than if it had to support any kind of "look-backwards and upwards" rules.
That said, what I'm referring to is actually supported by web-browsers: it's called the :has()
relational selector, however because of those performance reasons it isn't supported in CSS stylesheets, only in in the DOM's querySeletor
and querySelectorAll
functions:
I note that since 2021, some (non-Google) Chrome devs are
investigating ways to try to implement support for limited kinds of sub-selectors in :has()
in Chrome, read about it here: https://css-has.glitch.me/ - but I doubt support for this will enter Chrome for years, let alone other browser engines like Safari and Firefox.
Ideal solution: :has()
selector:
If we could use :has()
, then you could just do this and call it a day:
label[for]:has(input[type=radio]:checked) {
background: #EF8B22;
border: 1px solid #EF8B22;
box-shadow: 0px 0px 6px 0px rgb(239 139 4 / 30%);
}
Unfortunately they don't, so we can't do this.
Workaround: move your <input/>
elements:
The workaround is to hoist up your <input/>
to be located both above and before its container - but as you don't want the checkbox widget to be visually located there you'll need to either make the input invisible and use a replacement checkbox image in the <label>
or use a CSS technique to relocate the rendered input element, perhaps using CSS grid
or flex
(or even position
) but these don't give you much flexibility.
You can also use ::before
to add a fake checkbox/radio button (either using an aesthetically pleasing SVG or PNG as background-image
, or use CSS border
to render a box directly).
Something like this:
input { display: none; }
.gchoice_1_15_0,
.gchoice_1_15_1 {
width: 48%;
color: #58595B;
border: 1px solid #E4E4E4;
border-radius: 10px;
padding-left: 1em;
background: #fff;
transition: all ease-in-out .3s;
box-shadow: none;
}
#choice_1_15_0:checked ~ * label[for="choice_1_15_0"],
#choice_1_15_1:checked ~ * label[for="choice_1_15_1"] {
background: #EF8B22;
border: 1px solid #EF8B22;
box-shadow: 0px 0px 6px 0px rgb(239 139 4 / 30%);
}
/* CSS-only radio buttons: */
label::before {
display: inline-block;
content: ' ';
background: white;
border: 1px solid black;
border-radius: 7px;
box-sizing: border-box;
width: 14px;
height: 14px;
}
#choice_1_15_0:checked ~ * label[for="choice_1_15_0"]::before,
#choice_1_15_1:checked ~ * label[for="choice_1_15_1"]::before {
background: radial-gradient(circle, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 40%);
}
<input class="gfield-choice-input" name="input_15" type="radio" value="Yes" id="choice_1_15_0" onchange="/*gformToggleRadioOther( this )*/">
<input class="gfield-choice-input" name="input_15" type="radio" value="No" id="choice_1_15_1" onchange="/*gformToggleRadioOther( this )*/">
//Yes option
<div class="gchoice gchoice_1_15_0">
<label for="choice_1_15_0" id="label_1_15_0">Yes</label>
</div>
//No option
<div class="gchoice gchoice_1_15_1">
<label for="choice_1_15_1" id="label_1_15_1">No</label>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>