What is a star rating form element?
My specification for designing this element was as follows:
- The element should display a number of graphical stars.
- When a user clicks on a star, the clicked star and all stars to its left should light up.
- The element should submit as its value a numeric representing the number of selected stars.
- The element should only submit its value along with the rest of the form.
- It should still be usable as a form element should the user have javascript disabled.
01: Html Version
My first step at building this was to take an element that was similar to what I wanted to do to create a starting point from which to build the element. It would also serve as a useful fallback in case the user has javascript disabled. The closest html form element to what I wanted to do was the radio box, a number of radio elements with the same name but different values could be placed on the page to allow the user to select a number between 1 and 5. The code for this is below.
<form> <div class="stars"> <label><input type="radio" name="rating" value="1" id="rating_1" /><span>1 Star</span></label> <label><input type="radio" name="rating" value="2" id="rating_2" /><span>2 Star</span></label> <label><input type="radio" name="rating" value="3" id="rating_3" checked="checked" /><span>3 Star</span></label> <label><input type="radio" name="rating" value="4" id="rating_4" /><span>4 Star</span></label> <label><input type="radio" name="rating" value="5" id="rating_5" /><span>5 Star</span></label> </div> </form>
02: Designing a jQuery API
The next stage in building this element is to decide how the javascript will be used when someone wants to turn plain boring radio boxes into a star rating element. The javascript below shows a sample of how the plugin might be called.
$('.stars').starRating({ onimage: 'staron.png', offimage: 'staroff.png' });
03: Creating the plugin
To ensure the code can be reused we will be building the javascript as a jQuery plugin using the same style as described in that tutorial.
First we need to define the plugin and attach it to jQuery, I have also added the code for defining the default parameters and accepting the options passed into the plugin.
(function($) { $.fn.starRating = function(options) { // Defaults and options var defaults = { onimage: 'staron.png', offimage: 'staroff.png' }; var opts = $.extend({}, defaults, options); this.each(function() { }) } })(jQuery);
04: Setting up the element
When the plugin is called on an HTML element it should convert all the radio elements into star elements, we do this within the plugin's each loop. Here's how this is done:
$('input[type=radio]',this).hide(); //hide the radio buttons themselves $('label span',this).hide(); //hide the regular text labels $('label',this).append('<img src="'+opts.offimage+'">'); //create image elements
05: Handling user input
If you were to try out the code we have so far, it would create a nice looking set of five stars, however nothing happens when they are clicked on. What we now need to do is to add an event handler to the elements so that when a user clicks on a star it fills in the stars to its left. As we have used radio buttons for the base element and the image forms part of the label the users browser will take care of updating the value of the element for us, all that is left for us to do is update the user interface.
var changeHandler = function() { stars = parseInt($(this).val()); elementName = $(this).attr('name'); $('input[name='+elementName+']').each(function(){ if(parseInt($(this).val()) <= stars) { $('img',$(this).parent()).attr('src',opts.onimage); } else { $('img',$(this).parent()).attr('src',opts.offimage); } }); } $('input[type=radio]',this).bind('change',changeHandler); $('input[type=radio]:checked',this).trigger('change');
06: Putting it all together
The element is now complete, I have added the complete code for the plugin below. For a quick demo you can view it in action here.
(function($) { $.fn.starRating = function(options) { // Defaults and options var defaults = { onimage: 'staron.png', offimage: 'staroff.png' }; var opts = $.extend({}, defaults, options); this.each(function() { $('input[type=radio]',this).hide(); //hide the radio buttons themselves $('label span',this).hide(); //hide the regular text labels $('label',this).append('<img src="'+opts.offimage+'">'); //create image elements var changeHandler = function() { stars = parseInt($(this).val()); elementName = $(this).attr('name'); $('input[name='+elementName+']').each(function(){ if(parseInt($(this).val()) <= stars) { $('img',$(this).parent()).attr('src',opts.onimage); } else { $('img',$(this).parent()).attr('src',opts.offimage); } }); } $('input[type=radio]',this).bind('change',changeHandler); $('input[type=radio]:checked',this).trigger('change'); }) } })(jQuery);