jQuery Star Rating with ASP.NET MVC

4.71 (2816 votes)

There are a number of jQuery star rating plugins to choose from. All have their pros and cons. For this site, I decided to implement the one from FyneWorks because it allows for split stars. Here's how I did it.

The star rating system allows people to easily provide feedback on a blog item if they don't feel like submitting a comment. The rating plugin that I chose can be configured to allow any score to be applied, although I opted to allow people the provide a score from 1 - 5. I also wanted to give accurate information on the current average rating for each item, which meant that I need to show stars partially completed when an item's average rate is not a whole number. I also needed to prevent people rating an item more than once.

To begin with, I downloaded the plugin files, which consists of a number of .js files including jquery.MetaData.js, jquery.rating.pack.js and jquery.rating.js. There is also some documentation in the form of an html file, plus a style sheet for the rater and an image file for the stars. First thing I did was check the documentation for details on database integration, and was met with a message that this does not exist. The whole deal is completely up to me. Great. Then again, I suppose that there are so many options for connecting to databases - php to MySQL, ASP.NET to SQL Server via Linq To SQL, ADO.NET, Entity Framework, nHibernate, ColdFusion to whatever - that it would be a bit much to expect the authors to cover all angles. My method will be connecting to SQL Server via the Entity Framework, in keeping with previous articles on developing this site.

The rater will appear on the articles page. At the moment, there is nothing in the database to cope with rating articles, so I add two columns to the Articles table - Rating (int) and TotalRaters (int). As each rating comes in, I will increment the Rating value for the article by the score given by the rater. I will also add 1 to the TotalRaters for that article. I can calculate the average rating per article by dividing the Rating by the TotalRaters. I set the default value for both columns at 0. Having done that, I refresh my Model. The Article class is enhanced with the two extra properties:

I will be using a PartialView for the rater, so I create a class for the strongly typed ViewModel that it will use. The class will only contain 4 properties:

namespace MikesDotnetting.Models
  public class ArticleRating
    public int ArticleID { get; set; }
    public int Rating { get; set;}
    public int TotalRaters { get; set; }
    public double AverageRating { get; set;}

You may notice that this class is a subset of the full Article class properties (apart from the AverageRating property). You might be wondering why I am not using the full Article class. The reason is that I only need the data in this new class for the PartialView, and don't want to encumber it with more than necessary. Not only that, but exposing just these properties makes the PartialView more reusable.

Speaking of the PartialView, here it is:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ArticleRating>" %>
<script type="text/javascript">
  $(function() {
    $('.star').rating('readOnly', true);
    $('#rated').mouseover(function() {

      callback: function(value, link) {
          type: "POST",
          url: "/Article/Rate",
          data: $("#rate").serialize(),
          dataType: "text/plain",
          success: function(response) {
            if (response != 'false') {
              var data = eval('(' + response + ')');
              alert('Your rating has been recorded');
              $('#currentlyrated').html('Currently rated ' + data.AverageRating.toFixed(2) +
                ' by ' + data.TotalRaters + ' people');
            } else {
              alert('You have already rated this article');
          error: function(response) {
            alert('There was an error.');
  <div id="rated">
  <div style="float:left">
    <form id="Form1" method="post" action="">
      <input class="star {split:4}" type="radio" value="1" name="rating" <%= Utils.Check(0,0.25,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="2" name="rating" <%= Utils.Check(0.25,0.5,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="3" name="rating" <%= Utils.Check(0.5,0.75,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="4" name="rating" <%= Utils.Check(0.75,1,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="5" name="rating" <%= Utils.Check(1,1.25,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="6" name="rating" <%= Utils.Check(1.25,1.5,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="7" name="rating" <%= Utils.Check(1.5,1.75,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="8" name="rating" <%= Utils.Check(1.75,2,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="9" name="rating" <%= Utils.Check(2,2.25,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="10" name="rating" <%= Utils.Check(2.25,2.5,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="11" name="rating" <%= Utils.Check(2.5,2.75,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="12" name="rating" <%= Utils.Check(2.75,3,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="13" name="rating" <%= Utils.Check(3,3.25,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="14" name="rating" <%= Utils.Check(3.25,3.5,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="15" name="rating" <%= Utils.Check(3.5,3.75,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="16" name="rating" <%= Utils.Check(3.75,4,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="17" name="rating" <%= Utils.Check(4,4.25,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="18" name="rating" <%= Utils.Check(4.25,4.5,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="19" name="rating" <%= Utils.Check(4.5,4.75,Model.AverageRating) %>/>
      <input class="star {split:4}" type="radio" value="20" name="rating" <%= Utils.Check(4.75,5,Model.AverageRating) %>/>
    <p id="currentlyrated" style="float:left;padding-left:20px;">
    <%= Model.AverageRating > 0 ? "Currently rated " + Model.AverageRating.ToString("f") + " by " + Model.TotalRaters + " people" 
       : "<span style=\"color:red\">Not yet rated.  Be the first to rate this article!</span>"%>
  <div style="clear:both"></div>
  <div id="rater">
    <div style="float:left;">
      <form id="rate" method="post" action="">
      <input class="auto-submit-star" type="radio" name="score" value="1"/>
      <input class="auto-submit-star" type="radio" name="score" value="2"/>
      <input class="auto-submit-star" type="radio" name="score" value="3"/>
      <input class="auto-submit-star" type="radio" name="score" value="4"/>
      <input class="auto-submit-star" type="radio" name="score" value="5"/>
      <input type="hidden" name="ArticleID" value="<%=Model.ArticleID %>" />
    <p style="float:left;padding-left:20px;">
      Rate Now!
  <div style="clear:both"></div>

Right - before you simply copy and paste this and hope it will run, there is a fair amount to explain. I'll start with the second of the two divs. This is the 5 star rater which users will ise to score articles. I've used the option with a class of auto-submit-star, which according to the documentation is the one that does not need a submit button. It does need a bit of AJAX, which I will get to shortly. As well as the 5 stars for rating, the radio buttons are housed in a form which also contains a hidden field representing the ArticleID of the current article.

The previous div is the one that contains the split stars and will be shown to users when they first load an article. It will feature the current rating for that item. There are 20 of these, which when split into 4 represents 5 whole stars. Each star is capable of displaying ¼, ½, ¾ or a full star. That's as accurate as I need for display purposes. You will notice the <%= Utils.Check(0,0.25,Model.AverageRating) %> which appears in each input tag. This refers to a utility method that returns a string: checked="checked" if the average rating falls between two values. Here is the code for the method. It's dead simple:

public static string Check(double lower, double upper, double toCheck)
  return toCheck > lower && toCheck <= upper ? " checked=\"checked\"" : null;

I tried a number of ways to set the input as checked from client script, but in the end gave up. My lack of javascript knowledge eventually defeated me. I need to spend a lot more time examining the jquery.rater.js file, along with the MetaData.js file, I suspect.

Both the jquery.rater.pack.js file and the jquery.MetaData.js file are linked to in the main View, which is why they are not referenced in the Partial. The first segment of jQuery code sets the current rater (the first div) to disabled so that the user cannot use it to score with. It also hides the "live" rater, but adds a mouseover event to the disabled one which reveals the live rater, while hiding the disabled one. The message alongside the rater will change from a summary of the current score to one that invites users to Rate Now!

The second part of the jQuery code manages the AJAX form submission. Both the values from the form (the rate and the ArticleID) are serialized and posted to the Rate action on the Article Controller:

public ActionResult Rate(FormCollection form)
  var rate = Convert.ToInt32(form["Score"]);
  var id = Convert.ToInt32(form["ArticleID"]);
  if (Request.Cookies["rating" + id] != null)
      return Content("false");
  Response.Cookies["rating" + id].Value = DateTime.Now.ToString();
  Response.Cookies["rating" + id].Expires = DateTime.Now.AddYears(1);
  ArticleRating ar = repository.IncrementArticleRating(rate, id);
  return Json(ar);

Having taken the posted form values, the Action checks to see if there is a cookie called rating with the id of the article as part of its name, which would indicate that the user has rated this article previously. If there is, it sends back "false", but does not record the submitted score. If not, it creates a cookie showing that this article has been rated, and sets the expiry date of the cookie for one year's time. It then calls a method in the repository:

public ArticleRating IncrementArticleRating(int rate, int id)
  var article = de.ArticleSet
            .Where(a => a.ArticleID == id)
  article.Rating += rate;
  article.TotalRaters += 1;
  var ar = new ArticleRating()
               ArticleID = article.ArticleID,
               Rating = article.Rating,
               TotalRaters = article.TotalRaters,
               AverageRating = Convert.ToDouble(article.Rating)/Convert.ToDouble(article.TotalRaters)
  return ar;

which increments the Rating value by the score submitted, and the TotalRaters by 1. The updated values are then sent back to the Rate action to be serialized as JSON before being returned to the jQuery's success callback (repeated here to save you scrolling back up the page):

success: function(response) {
  if (response != 'false') {
    var data = eval('(' + response + ')');
    alert('Your rating has been recorded');
    $('#currentlyrated').html('Currently rated ' + data.AverageRating.toFixed(2) + 
      ' by ' + data.TotalRaters + ' people');
  } else {
    alert('You have already rated this article');
error: function(response) {
  alert('There was an error.');

If the response is not "false", it will be JSON. This is parsed using eval(), and while an alert is shown to the user confirming successful logging of their score, the paragraph containing the average rate is updated, and the rater is shown again.


There are some things that I would like to have included. The first is the ability to set the initial state of the split star rater via javascript so that I don't need the Check() helper method. That would also apply when an article has just been rated so that I can update the stars as well as the paragraph containing the revised average rating. However, I tried a number of ways to achieve this without success. Simply using jQuery to set the attribute on the relevant radio didn't work. It just stopped the starts being shown. Using FireFox to view the generated DOM didn't help much either. I really need to know more javascript than I do.

The other thing that I would like to have done is to apply a mouseout to the second div - the one that allows people to rate. Again, just applying a mouseout to the div with the id of rater had the desired effect, until the mouse moved from one star to the next. It seemed that instead of being applied to the div itself, the mouseout had been applied to each individual star (or generated div containing a star). The plugin code actually generates some DOM which consists of a span containing a series of divs and <a> elements which house the star images. I could not understand why the mouseout did not apply to the containing elements, and could not find a solution.

Date Posted:
Last Updated:
Posted by:
Total Views to date: 68777


- Jean

Thank you for your post, great input.
For one of the improvements you suggested:
$('#rated').mouseenter(function() {
$('#rater').mouseleave(function() {

Have a nice day,

- april

sorry, I clicked kick it button mistakenly, thought it was a part of output. the article rocks.thanks!

- hamid

Great Artice , please also provide the dowbloadable code with your articles

- emad

your articles are great i hope you provide a working code to be downloaded

- Iain

Would moving some of the rating generation code into a loop (jquery or asp) be a good idea?

- Dany

If user will clear all the cookies, then it will be available him to rate again.

- Adeel

i want to ask you that from where i download the style sheet . because i downloaded the jquery.rating.css but there is no class named "auto-submit-star" . please help me out. can u give me full source code file or only this css file that contain "auto-submit-star" class

- Mike


There is no css declaration for the .auto-submit-star option because I applied no styles to it. It is simply a convenient way to use jQuery selectors to create a wrapped set.

- Berra Bertsson

Nice article, one question though. After a rate is submitted, the stars aren't updated/repainted, only the text with the current average is replaces.

Any ideas on how to implement that?

- norman

thank's for the idea but it's simple..

- sivasankari


- mayuri

Great Article. Can you please provide me source code.

- Mike


All the code you need is included in the article.

- anand

good article.

- anand


Recent Comments

Rajasekar 24/11/2015 12:27
In response to Import Data From Excel to Access with ASP.NET
While use this code i'm facing on error: "Unrecognized Database format C:\mydabase.accdb" can any...

Parmod 24/11/2015 07:28
In response to ASP.NET 5 Project Basics
For a new learner (Fresher) in ASP.NET there is a issue Fresher have to learn two types of , old...

Robert 22/11/2015 21:35
In response to ASP.NET 5 By Numbers
I have to agree fully with Paul, this does sound like an entire mis-mash of technologies. Sort of in...

Christian 21/11/2015 15:46
In response to MVC 5 with EF 6 in Visual Basic - Creating an Entity Framework Data Model
Many thanks Mike to introduce me in the EF6 Code First way of thinking. Exactly what I need for my...

ax plains 20/11/2015 16:29
In response to Examining the Details and Delete Methods
Hello, really great tutorial for a beginner like me! Is it possible to have an explanation on how a...

Abdul Latif 20/11/2015 14:42
In response to Reading Excel Files Without Saving To Disk In ASP.NET
Could anyone please help me, I am getting : "OfficeOpenXml.ExcelPackage" does not contain a for...

Thomas 20/11/2015 09:44
In response to Cheat Sheet - .NET Framework Exceptions
Hey Mike, nice list. I've also checked your article on how you created the list, but is there any to...

Pramod Gagare 19/11/2015 11:18
In response to Date Formatting in C#

Robby 19/11/2015 08:16
In response to WebMatrix - Database Helpers for IN Clauses
Would it also be possible to override the default query and querysingle methods to include the azure...

Menja 18/11/2015 08:28
In response to Sessions and Shopping Carts
Thank you for a perfect description and that you show all the screenshot at the same time!!! It a...