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(
-
- function() {
- $(this).prevAll().andSelf().addClass('ratings_over');
- $(this).nextAll().removeClass('ratings_vote');
- },
-
- 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.