3

I wanted to create a custom radio button using ::after. My HTML structure looks like this:

<div class='radio'>
  <input type='radio' class='radio-input'>
</div>

and what I want is to add an ::after to the radio element and apply some style when the input is checked. What I wrote (and is not working) is this:

.radio-input:checked ~ .radio::after {
  ...
}

If I use an element instead of the ::after it works but I wanted to know why this doesn't.

TylerH
  • 19,065
  • 49
  • 65
  • 86
SKOLZ
  • 133
  • 1
  • 10
  • 2
    http://stackoverflow.com/questions/2587669/can-i-use-the-after-pseudo-element-on-an-input-field – Nenad Vracar Nov 07 '16 at 17:47
  • It's not related to the combination of selectors, but merely the fact that `input` elements don't have `::before` and `::after` pseudo elements. – GolezTrol Nov 07 '16 at 17:50
  • 1
    You're using a general sibling selector (~) but `.radio` and `.radio-input` are not siblings. `.radio-input` is a child of `.radio`. – denmch Nov 07 '16 at 18:02
  • thanks @denmch, but the `::after` pseudoelement of the `.radio` element is a sibling of the `.radio-input` It doesn't work like that, does it? – SKOLZ Nov 07 '16 at 20:49
  • @GolezTrol the `::after` is added to the div with class radio, not to the input itself. – SKOLZ Nov 07 '16 at 20:50
  • I think the question is due to a misunderstanding of `~`, so maybe [What does the “~” CSS selector mean?](http://stackoverflow.com/q/10782054/1529630) or [Is there a CSS parent selector?](http://stackoverflow.com/q/1014861/1529630) would be more appropriate dupetargets. – Oriol Nov 07 '16 at 20:55
  • 1
    @SKOLZ Pseudo-elements aren't in the DOM (they're not real elements but just appear that way) so they can't be siblings of anything. The effect you want may be achievable by using `:checked::after` and setting the resultant pseudo-element with appropriate display, padding, etc. to do whatever it is you're after. – denmch Nov 07 '16 at 21:02
  • Input elements **do support** CSS generated content (aka pseudo-elements). However, what I can see is that OP is attempting to traverse the DOM tree in a way that is not supported by CSS (by either selecting the parent node, or the next `.radio` sibling of the parent. – Terry Nov 07 '16 at 21:06
  • thanks @denmch, since inputs can't have an `::after` pseudo element I'll use an empty element. You can check my radio buttons here: https://codepad.co/snippet/56ur2Elu – SKOLZ Nov 07 '16 at 21:08
  • In the example before I used the element with class `radio-fill` to replace the `::after` I wanted to use. Thanks @Terry, every time I add a pseudo-element to an input it does not work though :/ – SKOLZ Nov 07 '16 at 21:10
  • Sorry for the misunderstanding. Reopened the question. – GolezTrol Nov 07 '16 at 22:00
  • This is still a duplicate of the previous one. – TylerH Nov 07 '16 at 22:55
  • @BoltClock He's asking why `::after` doesn't work in this scenario. What he *thinks* is that he's trying to place it on another element (or even the closing *tag* of the wrapper div? I hope not), when in fact that's not really how ::after elements work. So it's really a two-part dupe. Part 1: Oriol's "What does the ~ selector mean" and Part 2: Can I use ::after on an input from Golez' first closure. – TylerH Nov 08 '16 at 04:16
  • @TylerH @BoltClock this is what I'm working on: https://codepad.co/snippet/56ur2Elu . You can see that I used an empty div there with class `radio-fill` to make the purple radio fill, if you check the css code there you will see that what I used to achieve this is the following css: `.radio-input:checked ~ .radio-fill` this is because I want the styles to apply to an element with class `radio-fill` that has a previous sibling which has the class `radio-input` and is checked. – SKOLZ Nov 08 '16 at 13:24
  • What I tried and failed is to add an after to the element with class `.radio` so it's content would be the input and an `::after` pseudo-element. this way I would target the `::after` instead of the empty div by doing this: `.radio-input:checked ~ .radio::after` which as far as I know means that I want the styles to be added to the `::after` pseudo-element of an element with `radio` class that's a sibling of an element with class `radio-input` and is checked` – SKOLZ Nov 08 '16 at 13:25
  • I think the link added by @BoltClock might help me, I think what's happening is what @Terry says. Since `::after` pseudo-elements are not in the DOM tree I can't target them using sibling selectors (not sure about that, but it makes sense). – SKOLZ Nov 08 '16 at 13:29
  • Well, the link that @BoltClock [added](http://stackoverflow.com/questions/7735267/can-i-target-a-before-or-after-pseudo-element-with-a-sibling-combinator) was what I needed, thanks man! – SKOLZ Nov 08 '16 at 13:47

1 Answers1

1

The reason the selector .radio-input:checked ~ .radio::after doesn't work with your markup is because you can't target ancestor elements, with any selector. Yours starts with the child (.radio-input), goes up to the parent (.radio) and then back down to the child pseudo. CSS cascades down, it doesn't ascend.

Instead give the input a true sibling. Here I use a label because it conforms to accessibility standards which make sure screen readers can interpret your form:

input:checked ~ label::before

with markup like:

<input type="checkbox" name="dubstep" id="dubstep" value="dubstep" tabindex="0">
<label for="dubstep">dubstep</label>

See example:

ul.check-or-radio-set {
  list-style-type: none;
  padding: 0;
  margin: 0;
        position:relative;
 }

    ul.check-or-radio-set li {
      padding:0;
      margin:0 0 10px 0;
      height:1.6em;
    }

 ul.check-or-radio-set input {
  position: absolute;
  left: -9999em;
 }

 ul.check-or-radio-set input ~ label::before {
  background: white;
  border: 2px white solid;
  box-shadow: 0 0 0 1px darkGray;
  content: '\a0';
  display: inline-block;
  line-height: 1;
  text-indent: .15em;
 }

 ul.check-or-radio-set input[type=checkbox] ~ label {
        position: relative;
        margin-left: 32px;
  line-height: 1.4em;
 }

 ul.check-or-radio-set input[type=checkbox] ~ label::before {
        position: absolute;
  margin-left: -32px;
  height: 1.4em;
  width: 1.4em;
  border-radius: 4px;
  transition: all .3s;
  background-image: url(http://imgh.us/checkmark-white.svg);
  background-position: 50%;
  background-repeat: no-repeat;
  margin-right: 10px;
 }

 /* checked */
 ul.check-or-radio-set input:checked ~ label::before {
  background-color: orange;
 }

 ul.check-or-radio-set input:focus ~ label::before {
  box-shadow: 0 0 5px 1px #007fea;
 }
 ul.check-or-radio-set input:disabled ~ label {
  color: lightGray;
  cursor: not-allowed;
 }
 ul.check-or-radio-set input:disabled ~ label::before {
  background-color: gray;
  box-shadow: 0 0 0 1px darkGray;
 }
<ul class="check-or-radio-set">
  <li>
    <input type="checkbox" name="rock" id="rock" value="rock" tabindex="0">
 <label for="rock">rock</label>
  </li>
  <li>
    <input type="checkbox" name="pop" id="pop" value="pop" tabindex="0" checked="checked">
 <label for="pop">pop</label>
  </li>
  <li>
    <input type="checkbox" name="dubstep" id="dubstep" value="dubstep" tabindex="0">
 <label for="dubstep">dubstep</label>
  </li>
</ul>

Note: I got this pattern from here: https://standards.usa.gov/form-controls/ - it's a great resource for making great looking, accessible form controls.

inorganik
  • 21,174
  • 17
  • 78
  • 101
  • Where did `label` come from? This is completely different code to what's provided in the question. – James Donnelly Nov 07 '16 at 22:26
  • @JamesDonnelly OP used div, but label is better for accessibility and semantics. – inorganik Nov 07 '16 at 22:28
  • @inorganik : thanks for the answer! what I'm trying to do is to avoid having an extra element since it's only for styling purposes. I also think that a div is a better option for what I'm trying to achieve because it represents a radio button fill and a label won't make much sense there. check this link to see what I'm working on: https://codepad.co/snippet/56ur2Elu – SKOLZ Nov 08 '16 at 13:34
  • @SKOLZ Gotcha. Sorry, I didn't really answer your question before. I re-read it, and edited my answer so that it actually answers you. Hope it helps! – inorganik Nov 08 '16 at 16:00