As part of a website rebuild, I recently created a really nice star rating system using pure css (with a healthy sprinkling of :hover attributes) to display the stars. It is entirely possible to configure the style sheet in such a way that hovering over one star causes the correct class changes and background image position changes to allow this functionality to work beautifully. Coupled with a couple of JQuery get commands and I had a really slick asynchronous rating system. The problem… it didn’t work in Internet explorer. Not only did the hover attributes mess up, but the coloured in and uncoloured stars did not overlap correctly, to name just two of the 8+ problems. I’m used to having to make extra stylesheets for IE to shift things around a few pixels but the sheer number of issues my page experienced had me rethinking whether it was fair for Microsoft to force so much extra work on me to fix what worked flawlessly in Safari, Firefox, Chrome and, with a tweak to one line of the css, Opera. But since around 45% of the visitors to the site in question use IE (a somewhat older demographic than myself) I needed to create some sort of solution to the IE problem.

So it was decided that due to the demographic in question, the asynchronous ratings that would save a page load might actually confuse the users (not everyone 20-30 years my senior uses Facebook with all its asynchronous updates!) so I ditched the rating system in favour of something quick and dirty. It doesn’t matter if it’s a complete hack, as long as it works for IE as well as proper browsers. So be warned, this is a completely ridiculous (IMHO) way of solving this problem but it does work. We’re trading my human effort off against the effort of users’ browsers having to execute copiously unnecessary amounts of Javascript instead of CSS.

Outline

The basic idea behind the Javascript hacky star rating is that all changes are initiated via javascript mouse event handlers rather than css. Thus, I still create a style sheet but don’t bother with any hover attributes. The current rating is stored in one div that is hidden by Javascript (allowing the rating to be made without Javascript enabled) and the proper stars are in another div that is unhidden by Javascript. We call these divs “noscriptRater” and “javascriptRater” respectively and assign this as both their id and class. We then use the following Javascript to select each of these by id and append “_js” to its class:

document.getElementById('noscriptRater').className='noscriptRater_js';
document.getElementById('javascriptRater').className='javascriptRater_js';

Note that .setAttribute(“class”,”noscriptRater_js”) will work for all browsers except IE, .setAttribute(“className”,”noscriptRater_js”) will work only for IE, hence I have used the .className as this works for all browsers. Facepalm again!

The following styles cause the visibility of the radio buttons and stars to swap when _js is appended to each class:

.noscriptRater, .javascriptRater_js { display:block; }
.javascriptRater, .noscriptRater_js { display:none; }

So now we’ve dealt with graceful degrading if JS is disabled, on to the actual dirty work.<br>The form that stores and submits the rating is relatively simple. Note that we have a dummy star0 div to allow an initial rating of zero stars as this gives a good user feel for an unrated set of stars. If you would like to allow users to actively choose this rating after a different rating has been selected, shift the input id=”rate0″ out of the div class=”hideStar0″ to allow this on the non-javascript version. To allow it on the star rating. We use div class=”hideStar0″ with the style .hideStar0 {display:none;} to hide this dummy star allowing a more generic approach to our code.

<form action="" method="post">
	<div class="noscriptRater" id="noscriptRater">
		<div class="hideStar0">
			<div id="star0"></div>
			<input type="radio" name="rating" value="0" id="rate0" checked>0<br>
		</div>
		<input type="radio" name="rating" value="1" id="rate1">0.5<br>
		<input type="radio" name="rating" value="2" id="rate2">1<br>
		<input type="radio" name="rating" value="3" id="rate3">1.5<br>
		<input type="radio" name="rating" value="4" id="rate4">2<br>
		<input type="radio" name="rating" value="5" id="rate5">2.5<br>
		<input type="radio" name="rating" value="6" id="rate6">3<br>
		<input type="radio" name="rating" value="7" id="rate7">3.5<br>
		<input type="radio" name="rating" value="8" id="rate8">4<br>
		<input type="radio" name="rating" value="9" id="rate9">4.5<br>
		<input type="radio" name="rating" value="10" id="rate10">5<br>
	</div>
	<input type="submit" value="Save">
</form>

To display the stars, we use the following HTML. Each star consists of a star container with an id in the form starN. We will use the background of this div to show off the star, whilst two child divs are our hot areas that can be hovered and clicked to make changes to the left or right half of the star (for half-star precision).

Note that if you would like to allow the user to provide a rating of zero stars, you should prepend the above html with the following snippet:

<div class="halfStarHolder">&nbsp;</div>

Now onto the Javascript that does all the work. The script has three components to it. The starClick function is executed when a user clicks to make a selection, resulting in it “sticking” in the star display and being stored in the radio buttons of the form. When a user hovers over any half of a star, the starHover function is fired, causing all the stars (and faux half stars) up to where the mouse is to light up and all those after it to grey out. The starReset function is fired when the user’s mouse leaves the stars, causing them to display the rating that is currently set in the form’s radio buttons.

/* 
	* Original file: starRating.js
	* Description:   A cross-browser compatible Javascript based star 
	*     rating system
	* Date:          11 June 2011
	* Author:        Rory Shillington
	* Source:        http://web.rory.co.nz
	* License:       Free to modify and use provided this comment 
	*     block remains intact. 
*/
function starHover(star,halfstar){
    var i;
    for(i=0;i&lt;star;i++){
        document.getElementById('star'+i).className=&quot;wholeStar&quot;;
    }
    if(halfstar==1){
        document.getElementById('star'+star).className=&quot;wholeStar&quot;;
    }else{
       document.getElementById('star'+star).className=&quot;halfStar&quot;;
    }
    for(i=star+1;i&lt;=5;i++){
        document.getElementById('star'+i).className=&quot;emptyStar&quot;;
    }
}
function starClick(star,halfstar){
    var val;
    val = 2*star;
    if(halfstar==0 &amp;&amp; val&gt;0){
        val--;
    }
    document.getElementById('rate'+val).checked=true;
    starHover(star,halfstar);
}
function starReset(){
    var i;
    var which;
    var val;
    for(i=0;i&lt;=10;i++){
        which=document.getElementById('rate'+i).checked;
        if(which){
            val=i;
        }
    }
    var star;
    var halfstar;
    star=Math.ceil(val/2);
    if(star==Math.floor(val/2) &amp;&amp; star&gt;0){
        halfstar=1;
    }else{
        halfstar=0;
    }
    starHover(star,halfstar);
}

Finally, we encapsulate all the HTML above inside a form tag and pop in a submit button to push the rating to the server as shown below:

<form action="rate.php" method="post">
    <!-- ALL HTML GOES HERE -->
    <input type="submit" value="Save">
</form>
Star matrix
Star matrix image

Finally, to actually make the stars colour correctly, I have created a an image with a matrix of stars on it:
We use CSS to add this to the background of each star element and adjust the background-position property to show the relevant part of it with the styles below. Using a single image for all possible star ratings means that hovering will never result in the user seeing a corrupted display while their browser downloads new star combinations.

.halfStarHolder{
    width:25px;
    height:50px;
    float:left;
}
.halfStar{
    background-position: 0 -50px;
}
.wholeStar{
    background-position: 0 50px;
}
.emptyStar{
    background-position: 0 0;
}
.halfStar, .wholeStar, .emptyStar{
    width:50px;
    height:50px;
    float:left;
    background-image:url("../images/big_star_matrix.png");
    overflow: hidden;
}
.noscriptRater, .javascriptRater_js{
	display:block;
}
.javascriptRater, .noscripRater_js{
	display:none;
}
.hideStar0{
	display:none;
}

If you would like to preselect your star rating, just add a ” selected” to the end of the appropriate radio button. If we execute the starReset() function around the time the page loads, this will then filter through to the stars.

starReset();

License
The star rating is free for you to modify and use as you see fit, as long as you keep the comments intact.