Question

I want to make a pure css [no jquery] star rating system. So I have a html form with 5 radio buttons. So far I can make when I hover/click on a star radio button to change it's own background image. It works for every star BUT separately. When I click on the fifth star - only the fifth star is changed. When I click on the fourth star - only the fourth star is changed and so on. I want to make that when I hover/click on the fifth star -> all stars to change their background image. When I click on the second star the first two stars to change and etc. so the user can see for how much stars he is voting. (you've seen these rating systems all over the internet)

I want to make like these (again only css):

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

But I end up with this:

enter image description here

So I have 5 stars/radio inputs and each is from a class:

1 star -> radioStarButton1

2 star -> radioStarButton2

3 star -> radioStarButton3

4 star -> radioStarButton4

5 star -> radioStarButton5

           <input type="radio" name="stars" id="'.'2stars-for-id-'.$row['id'].'" class="radioStarButton2" value="2" />
           <label for="'.'2stars-for-id-'.$row['id'].'"></label>

           <input type="radio" name="stars" id="'.'3stars-for-id-'.$row['id'].'" class="radioStarButton3" value="3" checked />
           <label for="'.'3stars-for-id-'.$row['id'].'"></label>

           <input type="radio" name="stars" id="'.'4stars-for-id-'.$row['id'].'" class="radioStarButton4" value="4" />
           <label for="'.'4stars-for-id-'.$row['id'].'"></label>

           <input type="radio" name="stars" id="'.'5stars-for-id-'.$row['id'].'" class="radioStarButton5" value="5" />
           <label for="'.'5stars-for-id-'.$row['id'].'"></label>

I want to make when I hover/click on the second class .radioStarButton2 to change also the fist class -> .radioStarButton1 and THAT is the problem - to change more than one class elements IF it is possible.

Also the css: This is working css:

input[type="radio"]{
display:none;
}

input[type="radio"] + label
{
    background-image: url("uncheckedstar.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}

.radioStarButton1:hover + label
{
    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}
.radioStarButton1:checked + label
{
    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}

The next code is obviously wrong but I don't know how to do it. I'm uploading it only for an idea what I'm trying to do:

.radioStarButton2:hover + label , .radioStarButton1
{

    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}
.radioStarButton2:checked + label , .radioStarButton1
{
    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}

.radioStarButton3:hover + label , .radioStarButton1 , .radioStarButton2
{

    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}
.radioStarButton3:checked + label , .radioStarButton1 , .radioStarButton2
{
    background-image: url("star.png");
    height: 23px;
    width: 23px;
    display:inline-block;
    padding: 0 0 0 0px;
}
Was it helpful?

Solution 3

An extension of Koen's solution ... and stays after clicked as well ... you need to re-position/style the labels (and add your stars), hide the inputs, but it's CSS only

Demo: http://jsfiddle.net/f82Fg/2/

HTML

<div class="butts">
    <input id="r5" type="radio" class="butt" value="5" />
    <label for="r5">5</label>
    <input id="r4" type="radio" class="butt" value="4" />
    <label for="r4">4</label>
    <input id="r3" type="radio" class="butt" value="3" />
    <label for="r3">3</label>
    <input id="r2" type="radio" class="butt" value="2" />
    <label for="r2">2</label>
    <input id="r1" type="radio" class="butt" value="1" />
    <label for="r1">1</label>
</div>

CSS

.butts {
    float: left;
}
.butt {
    display: block;
    float: right;    
    width: 16px;
    height: 16px;
}
.butt:hover,
.butt:hover ~ .butt,
.butt:hover ~ label {
    background-color: blue;
}
.butt:checked,
.butt:checked ~ .butt,
.butt:checked ~ label {
    background-color: red;
}

OTHER TIPS

If you place the HTML elements in reverse order, you can use the CSS sibling selector (~) to select the preceding stars.

HTML:

<div class="stars">
    <span class="star">5</span>
    <span class="star">4</span>
    <span class="star">3</span>
    <span class="star">2</span>
    <span class="star">1</span>
</div>

CSS:

.stars {
    float: left;
}

.star {
    display: block;
    float: right;

    width: 20px;
    height: 20px;
    margin: 5px;


    background: grey;
}

.star:hover,
.star:hover ~ .star {
    background: yellow;
}

http://jsfiddle.net/gfHX4/

You can only select “to the right” or “downwards” with CSS, not “to the left” or “upwards” – meaning, selecting descendant elements and siblings on the same level after an element in the DOM is possible; selecting ancestors or previous siblings is not.

So what you could do to get the star up to and including the one hovered to change color, is change color of all of them when the container element gets hovered – and then make the stars coming after the hovered one be gray again, using the ~ selector.

No for the “active”/radio-button-checked state, there is no easy way to do this – you could try to use the :target selector, but that would require the whole stuff to be nested into a couple of elements with different IDs, and it would lose the state again once some other hash-anchor on the page would be used.

To actually achieve that kind of thing, you will have to change the order of the stars, so that the first one displayed is the last one of them in DOM order, etc. – for example by floating them to the right within their container element, or by using direction.

That way, when the last element gets checked, only that one gets the orange color – and only the first in visible order will be orange.
The second-to-last gets checked, you format the second-to-last one and the last one (again using ~) – and due to reverse display order, the first to stars in visible order will be orange – etc.

The only problem left will be combining those two approaches – not exactly sure if that can be done easily.

Edit: OK, after seeing Koen’s answer, it should be quite easy, if you use the reverse-order trick for both :hover and :checked states.

You could place each label in a nested list and apply the hover state to include child elements like you would with a drop-down navigation.

However, you'd still need some scripting to mark any lower-level star input fields as selected once a user clicks on a higher-level star anyway. Otherwise, when they move the mouse off, the hover effect would disappear and you'd still have the same issue of only one star is shown as highlighted.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top