Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS rating stars issue with arrow keys

Tags:

html

css

I am having an issue with my rating stars system which is using pure CSS. My issue is that when I use arrow keys to select the stars... it is backwards. I have to use left arrow key to go right or right arrow key to go left. I tried playing around with the floats but it didn't help. I also tried rearranging the markup but I had similar results.

.rating {
  float: left;
}

.rating:not(:checked)>input {
  position: absolute;
  top: -9999px;
  clip: rect(0, 0, 0, 0);
}

.rating:not(:checked)>label {
  float: right;
  width: 1em;
  padding: 0 .1em;
  overflow: hidden;
  white-space: nowrap;
  cursor: pointer;
  font-size: 200%;
  line-height: 1.2;
  color: #ddd;
  text-shadow: 1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating:not(:checked)>label:before {
  content: '★ ';
}

.rating>input:checked~label {
  color: #f70;
  text-shadow: 1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating:not(:checked)>label:hover,
.rating:not(:checked)>label:hover~label {
  color: gold;
  text-shadow: 1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating>input:checked+label:hover,
.rating>input:checked+label:hover~label,
.rating>input:checked~label:hover,
.rating>input:checked~label:hover~label,
.rating>label:hover~input:checked~label {
  color: #ea0;
  text-shadow: 1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating>label:active {
  position: relative;
  top: 2px;
  left: 2px;
}
<fieldset class="rating">
  <legend>Please rate:</legend>
  <input type="radio" id="star5" name="rating" value="5" /><label for="star5" title="Rocks!">5 stars</label>
  <input type="radio" id="star4" name="rating" value="4" /><label for="star4" title="Pretty good">4 stars</label>
  <input type="radio" id="star3" name="rating" value="3" /><label for="star3" title="Meh">3 stars</label>
  <input type="radio" id="star2" name="rating" value="2" /><label for="star2" title="Kinda bad">2 stars</label>
  <input type="radio" id="star1" name="rating" value="1" /><label for="star1" title="Sucks big time">1 star</label>
</fieldset>

JSFiddle Demo

like image 486
TrickMonkey Avatar asked Nov 07 '22 18:11

TrickMonkey


1 Answers

As @randomguy04 already helpfully pointed out, the reversal is due to the radio-buttons being reversed in the DOM. As there is no way assign the buttons an Order in HTML, you will have to change your markup to contain the <input>'s in the right Order, for Example like this:

<fieldset class="rating">
    <legend>Please rate:</legend>
    <input type="radio" id="star1" name="rating" value="1">
    <input type="radio" id="star2" name="rating" value="2">
    <input type="radio" id="star3" name="rating" value="3">
    <input type="radio" id="star4" name="rating" value="4">
    <input type="radio" id="star5" name="rating" value="5">
    <label for="star5" title="Rocks!">5 stars</label>
    <label for="star4" title="Pretty good">4 stars</label>
    <label for="star3" title="Meh">3 stars</label>
    <label for="star2" title="Kinda bad">2 stars</label>
    <label for="star1" title="Sucks big time">1 star</label>
</fieldset>

The CSS required to make this work gets a little ugly, since CSS lacks selectors for parents and previous siblings, to make the rendering easier to optimize. Something like this would work:

.rating {
    float:left;
}

/*
 * :not(:checked) is a filter, so that browsers that don’t support :checked 
 * don’t follow these rules. Every browser that supports :checked also supports 
 * :not(), so it doesn’t make the test unnecessarily selective
 */
.rating:not(:checked) > input {
    position:absolute;
    top:-9999px;
    clip:rect(0,0,0,0);
}

.rating:not(:checked) > label {
    float:right;
    width:1em;
    padding:0 .1em;
    overflow:hidden;
    white-space:nowrap;
    cursor:pointer;
    font-size:200%;
    line-height:1.2;
    color:#ddd;
    text-shadow: 1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0,0,0,.5);
}

.rating:not(:checked) > label:before {
    content: '★ ';
}

.rating > input#star1:checked ~ label[for="star1"],
.rating > input#star1:checked ~ label[for="star1"] ~ label,
.rating > input#star2:checked ~ label[for="star2"],
.rating > input#star2:checked ~ label[for="star2"] ~ label,
.rating > input#star3:checked ~ label[for="star3"],
.rating > input#star3:checked ~ label[for="star3"] ~ label,
.rating > input#star4:checked ~ label[for="star4"],
.rating > input#star4:checked ~ label[for="star4"] ~ label,
.rating > input#star5:checked ~ label[for="star5"],
.rating > input#star5:checked ~ label[for="star5"] ~ label {
    color: #f70;
    text-shadow: 1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0,0,0,.5);
}

.rating:not(:checked) > label:hover,
.rating:not(:checked) > label:hover ~ label {
    color: gold;
    text-shadow: 1px 1px goldenrod, 2px 2px #B74, .1em .1em .2em rgba(0,0,0,.5);
}

.rating > input#star1:checked ~ label[for="star1"]:hover,
.rating > input#star1:checked ~ label[for="star1"] ~ label:hover,
.rating > input#star2:checked ~ label[for="star2"]:hover,
.rating > input#star2:checked ~ label[for="star2"] ~ label:hover,
.rating > input#star3:checked ~ label[for="star3"]:hover,
.rating > input#star3:checked ~ label[for="star3"] ~ label:hover,
.rating > input#star4:checked ~ label[for="star4"]:hover,
.rating > input#star4:checked ~ label[for="star4"] ~ label:hover,
.rating > input#star5:checked ~ label[for="star5"]:hover,
.rating > input#star5:checked ~ label[for="star5"] ~ label:hover,
.rating > input#star1:checked ~ label[for="star1"]:hover ~ label,
.rating > input#star1:checked ~ label[for="star1"] ~ label:hover ~ label,
.rating > input#star2:checked ~ label[for="star2"]:hover ~ label,
.rating > input#star2:checked ~ label[for="star2"] ~ label:hover ~ label,
.rating > input#star3:checked ~ label[for="star3"]:hover ~ label,
.rating > input#star3:checked ~ label[for="star3"] ~ label:hover ~ label,
.rating > input#star4:checked ~ label[for="star4"]:hover ~ label,
.rating > input#star4:checked ~ label[for="star4"] ~ label:hover ~ label,
.rating > input#star5:checked ~ label[for="star5"]:hover ~ label,
.rating > input#star5:checked ~ label[for="star5"] ~ label:hover ~ label,
.rating > input#star1:checked ~ label:hover ~ label[for="star1"],
.rating > input#star1:checked ~ label:hover ~ label[for="star1"] ~ label,
.rating > input#star2:checked ~ label:hover ~ label[for="star2"],
.rating > input#star2:checked ~ label:hover ~ label[for="star2"] ~ label,
.rating > input#star3:checked ~ label:hover ~ label[for="star3"],
.rating > input#star3:checked ~ label:hover ~ label[for="star3"] ~ label,
.rating > input#star4:checked ~ label:hover ~ label[for="star4"],
.rating > input#star4:checked ~ label:hover ~ label[for="star4"] ~ label,
.rating > input#star5:checked ~ label:hover ~ label[for="star5"],
.rating > input#star5:checked ~ label:hover ~ label[for="star5"] ~ label {
    color: #ea0;
    text-shadow:1px 1px goldenrod, 2px 2px #B74, .1em .1em .2em rgba(0,0,0,.5);
}

Note, that in radio buttons the up arrow selects the previous button, while the down arrow selects the next button. This behavior, which is very natural with regular radio buttons, might seem counter intuitive with your rating stars. I am afraid, however, that, using only CSS, there is no way to switch only the left and right arrow keys without switching top and bottom as well.

Fiddle

like image 127
Nuvanda Avatar answered Nov 15 '22 05:11

Nuvanda