How to Build a Star Ratings jQuery Plugin

Skill

How to Build a Star Ratings jQuery Plugin

Posted in:

Lately I've been working with jQuery plugins and I decided to create a tutorial demonstrating how to use the jQuery plugin system to build a simple star ratings control.

Almost all controls and features within jQuery are now built using the plugin architecture. It's actually a very simple system, and jQuery provides some pretty good documentation to get you started. Like always, let's start with an example of what we're going to build. Below I've got two star ratings controls with differing amounts of stars - one with 10 and one with 5.


Your Rating: not set


Your Rating: not set

Here's the source code required to build this example.

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="jquery.ratings.css" />
    <script src="jquery-1.3.2.min.js"></script>
    <script src="jquery.ratings.js"></script>
    <script src="example.js"></script>
  </head>
  <body>
    <div id="example-1"></div> <br />
    Your Rating: <span id="example-rating-1">not set</span>
    <br /><br />
    <div id="example-2"></div> <br />
    Your Rating: <span id="example-rating-2">not set</span>
  </body>
</html>

Just like any other jQuery control, all that's needed is an empty div to hold it. Here I've created two - one for the 10 star example, and one for the 5 star example. In the head of the document, we have to include all of our needed files: the CSS file that describes the look of our stars, jQuery itself, the ratings plugin, and the script that actually tells the plugin to create the controls.

Let's start with example.js.

$(document).ready(function() {
  $('#example-1').ratings(10).bind('ratingchanged', function(event, data) {
    $('#example-rating-1').text(data.rating);
  });
 
  $('#example-2').ratings(5).bind('ratingchanged', function(event, data) {
    $('#example-rating-2').text(data.rating);
  });
});

If you've worked with any jQuery UI controls, this should look pretty familiar. We first get a jQuery object for our example div and call ratings(), which will populate the div with the ratings control. Whatever number is passed into ratings will be used to set the number of stars. You can also pass a second parameter that sets the initial rating to something other than zero. We then bind to the 'ratingchanged' event and set the contents of our "Your Rating" label to the selected rating. And that's all that's required to create the ratings controls.

Next up is the CSS. This will be pretty important if you want your ratings control to look different than mine.

.jquery-ratings-star {
  width: 36px;
  height: 36px;
  background-image: url('empty-star.png');
  background-repeat: no-repeat;
  position: relative;
  float: left;
  margin-right: 2px;
}

.jquery-ratings-full {
  background-image: url('full-star.png');
}

jquery-ratings-star is used to set the look and feel for all stars. position and float should not be changed or the stars will be put in strange places, but everything else can be tweaked to get your desired look. jquery-ratings-full is simply used to set the look of the star when it is highlighted.

All right, now down to the meat of the plugin, jquery.ratings.js. The very first thing we need to do is tell jQuery about the new function, ratings.

jQuery.fn.ratings = function(stars, initialRating) {
  return this;
};

This function satisfies the bare minimum requirements for a new jQuery function. All new public functions are added to the jQuery.fn object. All new functions must return the jQuery object, unless otherwise stated. The function should also end with a semi-colon to prevent compression from breaking the code.

Unfortunately, this empty function doesn't do us any good. We need to actually start building some stuff. We'll be using the this object passed into the function in order to add our ratings control. Since this could be a selector containing more than one element, we need to iterate over it using each.

jQuery.fn.ratings = function(stars, initialRating) {

  //Save  the jQuery object for later use.
  var elements = this;
 
  //Go through each object in the selector and create a ratings control.
  return this.each(function() {
 
  });
};

The first thing I do is save this into a local variable. Because of some scoping issues later, this won't always refer to the jQuery object. Next up, I set up an each to loop through each element in the selector. Since each returns the jQuery object, I can simply return it from my function.

Next up we need to set up the container div that's going to hold our stars.

jQuery.fn.ratings = function(stars, initialRating) {

  //Save  the jQuery object for later use.
  var elements = this;
 
  //Go through each object in the selector and create a ratings control.
  return this.each(function() {
 
    //Make sure intialRating is set.
    if(!initialRating)
      initialRating = 0;
     
    //Save the current element for later use.
    var containerElement = this;
   
    //grab the jQuery object for the current container div
    var container = jQuery(this);
   
    //Create an array of stars so they can be referenced again.
    var starsCollection = Array();
   
    //Save the initial rating.
    containerElement.rating = initialRating;
   
    //Set the container div's overflow to auto.  This ensure it will grow to
    //hold all of its children.
    container.css('overflow', 'auto');
  });
};

Before I do anything, I make sure initialRating is initialized to something. If the user did not pass in a value, it will be set to 0. I then save the container element and a jQuery object for the container element to variables. Next, I create an array to hold all of my star objects. I then create and set a field on the container element to hold the selected rating. Lastly, I set overflow to auto so the container div will expand to hold the child objects.

We've finally made it to the point where we can build the stars.

jQuery.fn.ratings = function(stars, initialRating) {

  //Save  the jQuery object for later use.
  var elements = this;
 
  //Go through each object in the selector and create a ratings control.
  return this.each(function() {
 
    //Make sure intialRating is set.
    if(!initialRating)
      initialRating = 0;
     
    //Save the current element for later use.
    var containerElement = this;
   
    //grab the jQuery object for the current container div
    var container = jQuery(this);
   
    //Create an array of stars so they can be referenced again.
    var starsCollection = Array();
   
    //Save the initial rating.
    containerElement.rating = initialRating;
   
    //Set the container div's overflow to auto.  This ensure it will grow to
    //hold all of its children.
    container.css('overflow', 'auto');
   
    //create each star
    for(var starIdx = 0; starIdx < stars; starIdx++) {
     
      //Create a div to hold the star.
      var starElement = document.createElement('div');
     
      //Get a jQuery object for this star.
      var star = jQuery(starElement);
     
      //Store the rating that represents this star.
      starElement.rating = starIdx + 1;
     
      //Add the style.
      star.addClass('jquery-ratings-star');
     
      //Add the full css class if the star is beneath the initial rating.
      if(starIdx < initialRating) {
        star.addClass('jquery-ratings-full');
      }
     
      //add the star to the container
      container.append(star);
      starsCollection.push(star);
    }
  });
};

Here we have a simple loop to create stars based on the number passed in by the user. First up, I actually need an element to hold the star, so I create one using document.createElement. I then get a jQuery object from the new element. Next, I set the rating that this star represents and add the jquery-ratings-star css class. If the star is beneath the specified initial rating, I add the jquery-ratings-full class to it to make it highlighted. All that's left is to add it as a child to the container and push it into the stars array.

If you were to run the code right now, you'd see a bunch of gray stars. Let's hook up the events to actually make it work.

jQuery.fn.ratings = function(stars, initialRating) {

  //Save  the jQuery object for later use.
  var elements = this;
 
  //Go through each object in the selector and create a ratings control.
  return this.each(function() {
 
    //Make sure intialRating is set.
    if(!initialRating)
      initialRating = 0;
     
    //Save the current element for later use.
    var containerElement = this;
   
    //grab the jQuery object for the current container div
    var container = jQuery(this);
   
    //Create an array of stars so they can be referenced again.
    var starsCollection = Array();
   
    //Save the initial rating.
    containerElement.rating = initialRating;
   
    //Set the container div's overflow to auto.  This ensure it will grow to
    //hold all of its children.
    container.css('overflow', 'auto');
   
    //create each star
    for(var starIdx = 0; starIdx < stars; starIdx++) {
     
      //Create a div to hold the star.
      var starElement = document.createElement('div');
     
      //Get a jQuery object for this star.
      var star = jQuery(starElement);
     
      //Store the rating that represents this star.
      starElement.rating = starIdx + 1;
     
      //Add the style.
      star.addClass('jquery-ratings-star');
     
      //Add the full css class if the star is beneath the initial rating.
      if(starIdx < initialRating) {
        star.addClass('jquery-ratings-full');
      }
     
      //add the star to the container
      container.append(star);
      starsCollection.push(star);
     
      //hook up the click event
      star.click(function() {
        //set the containers rating
        containerElement.rating = this.rating;
       
        //When clicked, fire the 'ratingchanged' event handler.  
        //Pass the rating through as the data argument.
        elements.triggerHandler("ratingchanged", {rating: this.rating});
      });
     
      star.mouseenter(function() {
        //Highlight selected stars.
        for(var index = 0; index < this.rating; index++) {
          starsCollection[index].addClass('jquery-ratings-full');
        }
        //Unhighlight unselected stars.
        for(var index = this.rating; index < stars; index++) {
          starsCollection[index].removeClass('jquery-ratings-full');
        }
      });
     
      container.mouseleave(function() {
        //Highlight selected stars.
        for(var index = 0; index < containerElement.rating; index++) {
          starsCollection[index].addClass('jquery-ratings-full');
        }
        //Unhighlight unselected stars.
        for(var index = containerElement.rating; index < stars ; index++) {
          starsCollection[index].removeClass('jquery-ratings-full');
        }
      });
    }
  });
};

This is actually the complete code. We've now added all the events that are required to add interactivity to the ratings control. The first event is hooked to each star's click. Whenever a star is clicked, the container's rating is updated and the 'ratingchanged' event is triggered. The next event, mouseenter, makes sure the correct stars are highlighted. Whenever a star is moused over, all stars beneath is are highlighted and all stars greater than it are unhighlighted. The last event is hooked to the container. This event makes sure the correct number of stars are highlighted when the user's mouse leaves the control. The highlighted stars should revert back to the last selected state when the mouse leaves. This is done similar to the star's mouse enter event. Every star beneath the current rating is highlighted and every star above the current rating is unhighlighted.

And that's it. Now we've got a working jQuery plugin for star ratings. If you've comments, questions, or suggested feature additions to this control, leave them below or in the forums.

nomadjourney
05/19/2009 - 12:01

This has to be the easiest to use star rating jQuery plugin I have seen so far. Thanks!

reply

The Hairiest
06/12/2009 - 07:48

Hey Brandon, did you know you could have created a div like so: $('<div></div>'). And you can actually add attributes and whatnot as well.

reply

The Reddest
06/12/2009 - 09:03

My guess is createElement is faster since it doesn't need to parse the HTML before creating the DOM object, however I don't know for sure.

reply

Anonymous
07/11/2009 - 08:11

Hey Guys,
I need some specific sort of rating system for my website. Before I go discussing project details (which is based on this article but will require extra coding), needed to ask you if you accept custom project requests. Pricing can be set, I won't mind paying what you ask for.
skype adymate1.
Regards,
Andy.

reply

Anonymous
07/11/2009 - 08:12

And Yahoo - netproint at yahoo dot com
Andy.

reply

The Fattest
07/11/2009 - 09:24

Andy, go ahead and send us an email through our contact us link, we would be happy to at least discuss the possibility.

reply

Anonymous
08/13/2009 - 23:58

Hi guys,

I have tried to implement the star within the jQuery tab..however it stopped working..I can't even see the stars..But the stars work fine if I placed them outside the tab...

Tab that I am using is this plugin

http://stilbuero.de/jquery/tabs/

I've been tried to solve this issue for a week now...but I can't find any resources or solution...

Please help me on this issue..

Thank you so much.

reply

steve
10/08/2009 - 03:58

hi,
simple and easy to use plugin, good work!
but i had one problem when i used it like this:

jQuery('#example-1, #example-2, #example-3, #example-4').ratings(10).bind('ratingchanged', function(event, data) {
                jQuery(this).find("input").val(data.rating);
        });

it created all 4 ratings with full functionality and bind the ratingchanged to all 4, but it was changing only first one input.
so i rewrite it to get it worked:

jQuery('#example-1, #example-2, #example-3, #example-4').each(function (){
                jQuery(this).ratings(10).bind('ratingchanged', function(event, data) {
                        jQuery(this).find("input").val(data.rating);
                });
        });

is here any option to rewrite plugin to get it done with first piece of code or is there some mistake in my first code?
thnaks for reply,
steve

reply

madhu
12/22/2009 - 05:31

How to read the rated stars??

reply

wespai
01/13/2010 - 21:40

nice collect it to

http://ajax.wespai.com

reply

bistro7
01/18/2010 - 12:21

I will try this and provide feed back

reply

Sue
03/19/2010 - 07:15

I was looking for an accessible way to get started with jQuery and this tutorial was perfect - thanks!

reply

Ava
06/01/2010 - 15:03

where should I paste the formating code and the jQuery.fn.ratings function? Inside of head or body?
Thanks a lot!!!

reply

Bohdan
08/23/2010 - 16:15

Extremely useful tutorial! I slightly modified it to

//hook up the click event
star.click(function() {
//When clicked, fire the 'ratingchanged' event handler.  Pass the rating through as the data argument.
if (containerElement.rating == this.rating) {
  elements.triggerHandler("ratingchanged", {rating: 0});
  containerElement.rating = 0;
} else {
  elements.triggerHandler("ratingchanged", {rating: this.rating});
  containerElement.rating = this.rating;
}
});

this way allowing to 'unset' rating by clicking on the same star

reply

ehijon
09/03/2010 - 10:16

How to read the rated stars?

reply

ehijon
09/03/2010 - 10:28

I understand. Thank you!

reply

Anonymous
11/03/2011 - 08:54

i just wanted to save that rating value to an variable please help me on this ..................

reply

Anonymous
02/15/2011 - 10:33

many thanks for your article :)

but sorry, but how to read the stars after all? lets say we have a form and a hidden input field, how to update its value after user clicks the stars?

thank u in advance!

reply

The Reddest
02/15/2011 - 11:50

In the first javascript snippet, I bind to the ratingchanged event. Right now, the handler just updates the text of the field containing the selected rating. This would be a good place to update the value of the input field.

reply

Chris
11/14/2011 - 09:12

Could you please give an example?

reply

Anonymous
04/15/2011 - 14:46

Is Possible to simple modify this peace of code frome the first .js file to put the rating value into a .net lable?

$(document).ready(function() {
$('#example1').ratings(10).bind('ratingchanged', function(event, data) {
$('#examplerating1').text(data.rating);

});

reply

Anonymous
05/31/2011 - 10:33

Hey,

I have uploaded the rating to my database to create an overall rating. How do I grab that rating from the database and convert the data to stars?

reply

The Reddest
05/31/2011 - 10:56

The number of stars to display is passed in through the plugin.

$('#example-1').ratings(10 /*total*/, 5 /*rating*/)...

reply

Ted S
06/02/2011 - 17:11

Super helpful plugin, thank you!

I'm working on a page with 50 or so star ratings so I'd really rather not write a function for each.

I'm able to convert selecting over to a class rather than div easily and that gets the rating bars to work. However, I can't reference the results individually nor can I pre-set individual ratings with this approach. I am using a class on the element and id to separate the items.

$('.rating').ratings(10).bind('ratingchanged', function(event, data) {
  var id = $(this).attr("id");
  $("input[name='" + id + "']").val(data.rating);
});

The goal is to show 50 rating bars. Have each control a corresponding form field and pre-populate from a database record.

Any suggestions?

reply

Ted S
06/02/2011 - 17:21

Looks like I should have tried more before posting.

The code to accomplish this is:

$('.rating').each(function (){
                $(this).ratings(10).bind('ratingchanged', function(event, data) {

                var id = $(this).attr("id");
                alert('this div is' + id);
                $("input[name='" + id + "']").val(data.rating);
   
        });
  });

I am still working on how to pre-set the individual ratings.

reply

Ted S
06/02/2011 - 17:26

All solved. One silly ; had me all mixed up. Thank you for a great plugin!

reply

Anonymous
06/10/2011 - 06:07

How to set individual (starting) stars?

reply

imsam
06/14/2011 - 13:40

thank you for the plug in, right now im trying to put in a comment when a star is selected, example: when you select 1 star comment=worst, instead of a 1, and so on with each star, can you help me please

reply

gaflim
07/26/2011 - 18:04

hola Amigos de switchthecode..perdón por la molestia pero estoy trabajando con un sitio donde necesito implementar el sistema de votación mediante estrellas y ya me funciona pero tengo una consulta....ah, perdón, alguien habla español?

reply

Anonymous
09/28/2011 - 05:38

How do I store the ratings and associate it with each user in the database? I am using cakephp structure (MVC). I want to record each user ratings and store it in the user database in the column rating. Each user can vote any times, and the value will be updated only.

reply

TNX
10/10/2011 - 18:38

Nice work! Modded it a bit and added Bohdan's addition for easy resetting to make it perfect.

You did a great job... and it took ages for me to find it on Google. ;)

reply

Lucas
12/27/2011 - 23:11

but how do i insert the result on a Database?:

reply

Anonymous
01/09/2012 - 02:06

I am also looking for the same thing as you Lucas, anyone can help?

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.