Building a 5 Star Rating System with jQuery, AJAX and PHP

Building a 5 Star Rating System with jQuery, AJAX and PHP

Tutorial Details
  • Difficulty: Intermediate
  • Technologies: JavaScript, PHP, HTML, CSS
  • Estimated Completion Time: 30 Minutes

In this tutorial, you’ll learn how to build a rating system with AJAX, PHP, and jQuery. Votes will be recorded and updated in real-time with the magic of AJAX, and we’ll also leverage the power of PHP so that you don’t even need a database!


Step 1. Building the HTML

We’re going to create a simple page that lists two movies, and allows you to rate them. This means we need the stars to show the current rating, and to allow voting. We also want an area to show the total votes cast, and the current rating down to one decimal place.

Let’s take a look at the HTML/CSS

        <div class='movie_choice'>
            Rate: Raiders of the Lost Ark
            <div id="r1" class="rate_widget">
                <div class="star_1 ratings_stars"></div>
                <div class="star_2 ratings_stars"></div>
                <div class="star_3 ratings_stars"></div>
                <div class="star_4 ratings_stars"></div>
                <div class="star_5 ratings_stars"></div>
                <div class="total_votes">vote data</div>
            </div>
        </div>
        
        <div class='movie_choice'>
            Rate: The Hunt for Red October
            <div id="r2" class="rate_widget">
                <div class="star_1 ratings_stars"></div>
                <div class="star_2 ratings_stars"></div>
                <div class="star_3 ratings_stars"></div>
                <div class="star_4 ratings_stars"></div>
                <div class="star_5 ratings_stars"></div>
                <div class="total_votes">vote data</div>
            </div>
        </div>
    

Notice how there are no graphics in this HTML? They’ll be added with CSS. We’re just using the HTML to create the framework that the widget works from. Now it’s time to start adding CSS.

        .rate_widget {
            border:     1px solid #CCC;
            overflow:   visible;
            padding:    10px;
            position:   relative;
            width:      180px;
            height:     32px;
        }
        .ratings_stars {
            background: url('star_empty.png') no-repeat;
            float:      left;
            height:     28px;
            padding:    2px;
            width:      32px;
        }
        .ratings_vote {
            background: url('star_full.png') no-repeat;
        }
        .ratings_over {
            background: url('star_highlight.png') no-repeat;
        }
    

This first part of the CSS accomplishes a few things:

  • Gives the default ‘empty’ start to each star location
  • Sets up classes for filled in stars, and highlighted stars
  • Defines and styles the stars’ container.

You can either use the graphics provided in the download, or make your own. There needs to be a graphic for each of the three states: empty, full, and highlighted.

Next we add a little more CSS to position the total votes box, and center the widgets so the page matches the graphic at the start of this section.

        .total_votes {
            background: #eaeaea;
            top: 58px;
            left: 0;
            padding: 5px;
            position:   absolute;  
        } 
        .movie_choice {
            font: 10px verdana, sans-serif;
            margin: 0 auto 40px auto;
            width: 180px;
        }
    

Step 2. Adding the UI Interactivity

At this point, we have a very plain looking bunch of empty stars, but they don’t do a whole lot at this point. This is where jQuery comes to the rescue.

Our first step is to add mouseover and mouseout handlers for the stars. We need to highlight the star the mouse is over, and all the preceding stars.

        $('.ratings_stars').hover(
            // Handles the mouseover
            function() {
                $(this).prevAll().andSelf().addClass('ratings_over');
                $(this).nextAll().removeClass('ratings_vote'); 
            },
            // Handles the mouseout
            function() {
                $(this).prevAll().andSelf().removeClass('ratings_over');
                set_votes($(this).parent());
            }
        );
    

We’re taking advantage of jQuery’s powerful .prevAll() and .nextAll() methods to get the stars preceding and following the currently moused over star.

The code above then adds and removes the classes to make the stars under the mouse and before ‘highlighted’, and the stars after ‘not highlighted’.

What about set_votes() ?

This is a function that checks which stars should be in the ‘full’ state, and ties in closely with the next step, where we grab remote data from the server.


Step 3. Retrieving Data from the Server

Our stars highlight when you move the mouse over them, and that’s a great start. But what about the red stars showing the current vote? To reach this step, we need to both get the information from the server, and write some JavaScript to handle that data.

        $('.rate_widget').each(function(i) {
            var widget = this;
            var out_data = {
                widget_id : $(widget).attr('id'),
                fetch: 1
            };
            $.post(
                'ratings.php',
                out_data,
                function(INFO) {
                    $(widget).data( 'fsr', INFO );
                    set_votes(widget);
                },
                'json'
            );
        });
    

This code block – actually all the JavaScript – goes in a document.ready block. This particular code executes right away. It queries the server and gets some information on every vote widget on the page.

First we set up an object, out_data, to contain the information we’re sending to the server. Our PHP script expects to see ‘fetch’ when just grabbing data, so we include it here. We also include the ID of the widget, which lets the server-side script know what data we’re after. When the call back function fires, it contains a JavaScript object that looks like this:

        {
            "widget_id"     : "r1",
            "number_votes"  : 129,
            "total_points"  : 344,
            "dec_avg"       : 2.7,
            "whole_avg"     : 3
        }
    

The .data() method is a bit of jQuery magic that allows you to associate arbitrary data with a DOM
object.

If you look closely at the code, you’ll see we’re taking that object (stored in the variable INFO) and
doing something with it via the .data() method.

The .data() method is a bit of jQuery magic that allows you to associate arbitrary data with a DOM
object. In this case, we’re storing the data in the widget div. It can be accessed later like this:

        $('#one_of_your_widgets).data('fsr').widget_id;
    

set_votes(), Finally.

After the data has been returned from the server, its handed off indirectly to set_votes().

        function set_votes(widget) {
        
            var avg = $(widget).data('fsr').whole_avg;
            var votes = $(widget).data('fsr').number_votes;
            var exact = $(widget).data('fsr').dec_avg;
            
            $(widget).find('.star_' + avg).prevAll().andSelf().addClass('ratings_vote');
            $(widget).find('.star_' + avg).nextAll().removeClass('ratings_vote'); 
            $(widget).find('.total_votes').text( votes + ' votes recorded (' + exact + ' rating)' );
        }
    

The first three lines are for readability, as those variable names are pretty long. So let’s take a look at what’s happening here.

Line 7: ‘avg’ is a whole number, representing the rounded vote average of this widget. Because it’s
a number 1-5, we can use it to find the proper star in the widget, and turn it, and the
preceding ones to our ‘filled’ graphic. Notice the use of .andSelf() to include the star that
we’ve selected.

Line 8: This is quite similar to line seven, but we’re removing the filled graphic from later stars. This
is necessary in case the average for this widget has gone down since the last vote.

Line 9: Here we’re updating the grey box underneath the widget, which shows a more precise rating,
and lets a visitor know how many votes have been cast.


Step 4. Let the Voting Begin

The final step for the UI is to enable voting. We’re going to add a click handler to each of the stars. This click handler will be responsible for sending the vote data to the server.

Here’s the click handler:

        $('.ratings_stars').bind('click', function() {
            var star = this;
            var widget = $(this).parent();
            
            var clicked_data = {
                clicked_on : $(star).attr('class'),
                widget_id : widget.attr('id')
            };
            $.post(
                'ratings.php',
                clicked_data,
                function(INFO) {
                    widget.data( 'fsr', INFO );
                    set_votes(widget);
                },
                'json'
            ); 
        }); 
    

In this code block, we start out by creating some variables not only for clarity, but, in this case, so they can be used within the .post callback. Remember the click handler is assigned to the stars, so we also need that second variable, widget, to have the object containing the data.

First, we set up our outgoing data, which we place in the object clicked_data. We grab the class which includes a class name in the format of star_# telling us what vote is being given, and prepare to send that to the server, along with the widget’s ID.

The widget ID is the corner stone that this voting system relies on. It allows us to look up our stored data, and to easily show that data to the visitor.

Finally, on line line, we send this information to the server. The server will add the vote to the current totals, and send information back to the browser containing the updated data. The values displayed by the widget are then updated with set_votes().


Step 5. PHP: Creating the Class

Now that the UI is finished, we need to create a server side script to store and retrieve voting data.

We’re going to create a very simple class in PHP, called ‘Ratings,’ and use it to handle server requests for our rating system. There are only going to be two methods, plus the invocation. The use of our class will look like so:

        # New Object
        $rating = new ratings($_POST['widget_id']);
    
        # either return ratings, or process a vote
        isset($_POST['fetch']) ? $rating->get_ratings() : $rating->vote();
    

If you go back to section four, you’ll see we load the data with the variable ‘fetch’ set – that’s what we’re looking for here on line five. If its not set, then we’re processing a vote.

The first thing we’re going to look at is the begining of the class, and, more specifically, the constructor.

        class ratings {
            
            private $data_file = './ratings.data.txt';
            private $widget_id;
            private $data = array();
               
        function __construct($wid) {
            
            $this->widget_id = $wid;
        
            $all = file_get_contents($this->data_file);
            
            if($all) {
                $this->data = unserialize($all);
            }
        }      
    

serialize() and unserialize are a great way to easily store
PHP data structures on disk.

There’s a lot going on here in very few lines, so I’m going to cover the important bits.

Line 3: This needs to be set to a text file you’d like to use to store your data. We’re not using a database for this project, although you easily could. A simple file will suffice for our needs.

Line 7: The constructor. This is called when we create our object, and immediately stores the ID of the widget.

Line 11: We try to load the text file. If the file doesn’t exist, fine, but on some systems you’ll need to create it ahead of time and give it the proper permissions for PHP to be able to read and write to it.

Line 14: This line is important. It takes the data from the text file – if there is one – and unserializes() it. The file contains a complex PHP array that’s been converted to a plain text representation, via serialize(), allowing us to store it and read it back in as an array later.


Step 6. The get_ratings() Method.

This method is called either on its own, or from the vote() method. It finds the data for a particular widget ID and returns it to the requesting page, in JSON format.

    public function get_ratings() {
        if($this->data[$this->widget_id]) {
            echo json_encode($this->data[$this->widget_id]);
        }
        else {
            $data['widget_id'] = $this->widget_id;
            $data['number_votes'] = 0;
            $data['total_points'] = 0;
            $data['dec_avg'] = 0;
            $data['whole_avg'] = 0;
            echo json_encode($data);
        } 
    } 
    

This only looks complicated – it’s actually pretty simple. The first thing we do is check if the array stored in $this->data has a key matching our widget ID. If it does, we just return that information, because that’s the widget data the page was requesting.

We don’t have to do anything to that data because its already in array form. $this->data is just an array of arrays. We encode the array we want with json_encode() and send it back to the browser.

If there’s no data for the widget ID we’ve requested, we create a record with all zero values, and send it back to the browser.

Step 7. The vote() Method

Next, we need to create a method to handle incoming votes. When the method finishes, it has to call get_ratings() to send the updated information back to the web browser.

The Method Start

        public function vote() {
            # Get the value of the vote
            preg_match('/star_([1-5]{1})/', $_POST['clicked_on'], $match);
            $vote = $match[1];   
        
    

The first thing we do is get the value of the vote. Remember that somewhere in ‘clicked_on’ is a class name in the format of star_#. "star_4", for example. To get that value, we’re using a regular expression and capturing the value of the number to $match[1].

The method Middle

    
            $ID = $this->widget_id;
            # Update the record if it exists
            if($this->data[$ID]) {
                $this->data[$ID]['number_votes'] += 1;
                $this->data[$ID]['total_points'] += $vote;
            }
            # Create a new one if it does not
            else {
                $this->data[$ID]['number_votes'] = 1;
                $this->data[$ID]['total_points'] = $vote;
            }
    

Here we store $this->widget_id into $ID for clarity – the following code gets a bit rough on the eyes without it.

We check if information for this ID exists, and, if so, we add a vote to the total vote count, and add the points from the vote received. This is a running total of all votes; so if one person gives five stars, and another, three, that’s eight points total.

If the record doesn’t exist, we create one, with one vote, and just the points from the incoming vote.

Finishing Up

  
            $this->data[$ID]['dec_avg'] = round( $this->data[$ID]['total_points'] / $this->data[$ID]['number_votes'], 1 );
            $this->data[$ID]['whole_avg'] = round( $this->data[$ID]['dec_avg'] );
                  
            file_put_contents($this->data_file, serialize($this->data));
            $this->get_ratings();
        }
    

Once we’ve updated the vote and point totals, we have to calculate both the average expressed as a whole number, and to one decimal point. To avoid having to do the math twice, we first calculate the average to one decimal on line one, and then round that off to a whole number, on line two.

On line four, we’re storing the changed information back on disk after processing it with serialize(). Once the data is safely stored away, we call $this->get_ratings() to send the new, updated information to the browser.


Conclusion

For the sake of simplicity, this isn’t a 100% complete solution. To extend this project, we should store a cookie to make sure people only vote once, or even record the IP address. It’s also possible that two first-votes couple happen simultaneously, and only one may be recorded. It is, however, a great start, and is more then suitable for keeping track of votes on a few handfuls of items on your website. Thoughts? Thanks for reading!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://jeffrey-wright.com jeff wright

    hey.

    okay, so I’ve did a couple of changes to this code, nothing too instense. just some css stuff. I dont know why, but the ratings are repeating themselves when you refresh the page? see http://www.isanyonedown.com/ratings/page.html

  • http://ierolohitis.wordpress.com octavian purice

    10x for sharing. i found it very useful

  • http://www.tbbtire.com Derick

    It’s excellent tutorial I have been looked for. Thanks for sharing.
    But…I am very new on these. I just uploaded whole file to my server, do nothing with database, it won’t work. So I think I might miss something with database. What should I do? Is there someone can help me out?
    Thanks in advance!

    Derick

  • http://www.tbbtire.com Derick

    Hello All,
    There is no sql file, which file will connect to server? how to use ratings.data.txt?
    Thanks!

  • L

    Hi,

    How about rating system with like/dislike ? +/ -

  • http://www.creative-dynamics.eu Darren

    Looks Perfect! Has there been any improvements since last year?

    What if there were 25 people in different locations clicking continuously?

    Would the rating update correctly?

    Sounds like a stupid question I know but the place where I want to use it, this is a likely scenario!!

  • http://www.jjjock.me Jockular

    You can easily game the system on this by adding following js code into your address bad:

    javascript:setInterval(function(){$(‘.star_5′).click()},0);void(0);

  • Luigi

    I put this code in a pdf form?

    http://www.mediasitalia.info/prova/prova.pdf

  • Dhruv

    Guys are you not getting any error while running this code??
    I am getting an error somewhere in the function set_votes(). The error exists in the first line of creating the set_votes() function itself. Please help me.
    Do tell me how to use the files rating.php and ratings.data.txt.

  • http://www.computerman.gr/ Petsoukos

    Thank you!

    I’ve based my Codeigniter rating system from this tutorial.

    Which method is the best for limiting a user to only one vote? Cookie or IP? Or is there something else?

  • Adroxxx

    How to make a user vote only one time?

  • http://www.saqibdesigns.info mohd saqib

    Nice info…. actually today I need to use this type of star rating in one of my company’s site…. but its looking very complicated in reading…. well will try it..

    Thanks for share :)

  • http://www.creatonsolutions.com Sam

    Thanks Greate article on combining 3 languages together adn produce result.

  • Soru

    The Rating goes to previous value once we reset the page ..
    what will happen when the page is refersed.

  • http://railway123.com rashid

    thats nice..thanks

  • Randall

    This rating system is not prepared for the javascript inyections like Jockular sayed.
    Easy to crash only tiping:
    javascript:setInterval(“$(‘.star_1′).click()”,100)
    or
    javascript:while(true)$(‘.star_1′).click()

  • Alfredo

    Hi,
    Great job, can i have help for the click of stars?????
    Thanks,
    Alfredo(Sorry for my english)

  • http://www.silicagelpacks.com Udhaya47

    I will try this in my site…Thanks

  • http://www.coursesweb.net CoursesWeb

    Hi,
    I tried to make a similar star rating script, with the rating data saved in TXT files, or MySQL database.
    This is what I made http://www.coursesweb.net/php-mysql/rating-stars-script-ajax-php_s2 , after a few days of coding :).

  • Serge

    Hello,

    I found your rating script here (http://www.coursesweb.net/php-mysql/rating-stars-script-ajax-php_s2 , ) very nice. How I can change it so it can contain 7 stars instead of 5 stars ?

    Hope to hear from you soon.

    Cheers!

    Marius

  • http://www.google.com 123

    wow great work.Weldone

  • http://www.mrblackscript.3owl.com polas

    i tried CoursesWeb your star no errors but also do not show anything just black page

  • Hakan

    How to add ip control

  • http://comparatorehosting.com salcapolupo

    Really great explanation, thanks so much!

  • http://boldprintdesign.com Susan

    Hi there. Your demo is gone! Can you please provide a new link? Thanks.

  • http://mobilemoviesreview.com/ Dwayne

    Can you make a tutorial video it’s kinda hard to understand from just reading here on your site.

  • http://templatz.co Kev

    Not bad but I don’t really like the stars.

    There is a 5 star rating system at the following link you may want to have a look at. It has glossy or glass-like stars, very modern.

    http://templatz.co/5-star-rating-system.php

  • Micky

    I copied the demo files to the server, unchanged and the sample page.html file isn’t working. I wonder why….

  • Micky Fokken

    I got it working! Remember to change the permissions of the txt file after it is uploaded to the server. chmod 0777. Or in Dreamweaver, right-click the txt file on the server > go to permissions > and check all the boxes.

    A little tip for a newbie like myself. Maybe that was in the instructions, and I just missed it!

  • http://www.hanoiproperty.com David Do

    many thanks for sharing this post, I will try it.

  • http://templatz.co Kev

    Check out this star rating system. It has 9 different cool stars you can use including black stars and pumpkins.

    http://templatz.co/rate-it-templatz/

  • Kevin

    Where do you put this?

    {
    “widget_id” : “r1″,
    “number_votes” : 129,
    “total_points” : 344,
    “dec_avg” : 2.7,
    “whole_avg” : 3
    }

  • blackmwana

    i was trying out this tutorial but there seemed to be a problem with getting data when the txt file is empty so i fixed it like this

    //in the get ratings method
    if (!empty($this->data) && $this -> data[$this ->entry_id] ) {// check if the data array is empty or not
    //if($this -> data[$this ->entry_id]){
    echo json_encode($this -> data[$this -> entry_id]);
    // }
    }

    and in the vote method

    if (!empty($this->data) && $this -> data[$ID]) {// check data array again
    $this -> data[$ID]['number_votes'] += 1;
    $this -> data[$ID]['total_points'] += $vote;
    }

    maybe this is unnecessary but my code was not working without it

    • blackmwana

      i left out something ,using the isset($array(index)) to check whether the item is in the array or not set at all, again my code wasnt working properly without this

      //in the get ratings method
      if (!empty($this->data) && isset($this -> data[$this ->entry_id] )) {// check if the data array is empty or not
      //if($this -> data[$this ->entry_id]){
      echo json_encode($this -> data[$this -> entry_id]);
      // }
      }

      and in the vote method

      if (!empty($this->data) && isset($this -> data[$ID])) {// check data array again
      $this -> data[$ID]['number_votes'] += 1;
      $this -> data[$ID]['total_points'] += $vote;
      }

      • itchyitch

        I’m also having the same problem where do I implement this code?

  • http://ninjakimal.com ninjakimal

    Thank you for this post. I’ll try this for my next project

  • http://net.tutsplus.com/tutorials/html-css-techniques/building-a-5-star-rating-system-with-jquery-ajax-and-php/ savitha

    nice

  • http://templatz.co Kev

    Nice article. If you’re looking for a 5 Star Rating System but with different stars, we got you covered. We have crystal stars, Christmas stars, dark stars, oxygen stars, plain stars, pumpkin stars and more. All the stars come in the download and are easily switched. Check it out!

    http://templatz.co/5-star-rating-system.php

  • amit0535

    nice

  • Wassim Azzouzi

    good one, easy to do.

  • kuldeep

    hi

  • Israel

    Works just fine. Great ’5 star Rating’ system. Thanks for sharing it!

  • fatih

    how to add a review system additionally? and how to pull the average rating to an external table ?

  • sdf

    test

  • Philip

    I really would like this to connect with the database, but I really don’t know how to do that.. The tutorial says that it is very easy, so can anybody tell me exactly which line I need to change to what?

  • Alex

    How do you make people vote only once? please provide steps.

  • Kevin Short

    5, 7 and 10 Star Rating System – Saves user ratings in a text file by default but is also set up for MySql database. Three scripts included.

    You will also find an Up Down Voting System.

    http://templatz.co/rating-voting-scripts.php

  • Guest

    before it and

  • clarencelouie

    i want to integrate it to my website. nice post

  • jack moore

    l newly complete a review project within a Jquery 5 star rating plugin, it works very well and You can visit demo here http://jquerystarrating.com/demo.php

  • Sanjeev

    Nyc code but can i know how to show point system rating like 3.5 then it should be show 3 and half star voted .?