If you are a regular at Reddit, you must have noticed the way people vote there. You can either vote up or vote down. Pretty interesting, huh? This tutorial will show you how to create such a voting system with jQuery, PHP and MySQL.
Tip: If you are looking for a more secure, easy to integrate and customise, free solution, you might like to check the open-source alternative Pulse Lite.
You might also like these tutorials:
Download Source Try Demo

Let's Get Started

We need a table in the database to fetch the data from. Let's create the table.
I created a table called entries where there are 5 columns:
  • id — The unique id associated with each entry
  • title — The title of the entry to be shown as a link to the site
  • link — The link to the site
  • votes_up — The total number of people who voted up.
  • votes_down — The total number of people who voted down.
In order to test the application, we insert some dummy data.

Fetch The Data

Now, we have the table and the data. Let's show the data.

We create a config.php file that holds all our database credentials.

 $hostname = "localhost";
 $db_username = "your_username";
 $db_password = "your_password";

 $link = mysql_connect($hostname, $db_username, $db_password) or die("Cannot connect to the database");
 mysql_select_db("your_database") or die("Cannot select the database");
What we did is just connect to the database. Do replace your_username, your_password and your_database with your own values.

We create an index.php file.
 <script type='text/javascript' src='jquery1.3.pack.js'></script>

Display the results from the database
$q = "SELECT * FROM entries";
$r = mysql_query($q);

if(mysql_num_rows($r)>0): //table is non-empty
 while($row = mysql_fetch_assoc($r)):
  $effective_vote = $row['votes_up'] - $row['votes_down']; //this is the effective result of voting up and voting down
<div class='entry'>

 <span class='link'>
  <a href='<?php echo $row['link']; ?>'> <?php echo $row['title']; ?> </a>
 <span class='votes_count' id='votes_count<?php echo $row['id']; ?>'><?php echo $effective_vote." votes"; ?></span>
 <span class='vote_buttons' id='vote_buttons<?php echo $row['id']; ?>'>
  <a href='javascript:;' class='vote_up' id='<?php echo $row['id']; ?>'></a>
  <a href='javascript:;' class='vote_down' id='<?php echo $row['id']; ?>'></a>


First, we include the config.php file to get access to database. Then we query the database to show all the entries found in the table. Next, we loop through the resultset and create a div for each entry.

Notice the id attribute of both votes_count and vote_buttons classes. Since they id attributes are suffixed with the <?php echo $row['id']; ?> , it would be different for different entries which would be helpful to identify each entry. Also the id of both vote_up and vote_down links are unique for each entry.

A Little Bit Of CSS

Without proper styles our index.php is looking ugly.

body {
 background: #e8e6de;

a {

.entry {
 width: 710px;
 background: #ffffff;
 border:1px solid #bbbbbb;
 margin:5px auto;

span.link a {
 color: #000000;

a.vote_up, a.vote_down {

a.vote_up {

a.vote_down {

Notice, we added text-indent:-900% to vote_up and vote_down classes. This is a nice trick to hide the text and coming clear to search engines.

Now if you view the index.php in browser it would show you the entries and you should land with something like this:

Make Ajax Calls

Ok, now we are going to make it more interesting with Ajax. Our objective is when the user clicks on the vote-up or vote-down link, the request will be sent to the server through Ajax; a simple script then updates the vote-count and displays the effective votes. This effective vote-count will then be displayed to the user through a simple jQuery effect.

But first we create votes.php file and in it, write two functions that would be useful.

include("config.php"); //config.php is added to get access to database connection
function getAllVotes($id)
 Returns an array whose first element is votes_up and the second one is votes_down
 $votes = array();
 $q = "SELECT * FROM entries WHERE id = $id";
 $r = mysql_query($q);
 if(mysql_num_rows($r)==1)//id found in the table
  $row = mysql_fetch_assoc($r);
  $votes[0] = $row['votes_up'];
  $votes[1] = $row['votes_down'];
 return $votes;
This function is pretty simple. It expects an integer as its parameter and selects all the data from the table matching the id. Then it returns an array whose first element is votes_up and the second one is votes_down.

function getEffectiveVotes($id)
 Returns an integer
 $votes = getAllVotes($id);
 $effectiveVote = $votes[0] - $votes[1];
 return $effectiveVote;
This function depends on the first one and calculates the difference between votes_up and votes_down.

Now, we add the following code to votes.php file:
$id = $_POST['id'];
$action = $_POST['action'];

//get the current votes
$cur_votes = getAllVotes($id);

//ok, now update the votes

if($action=='vote_up') //voting up
 $votes_up = $cur_votes[0]+1;
 $q = "UPDATE entries SET votes_up = $votes_up WHERE id = $id";
elseif($action=='vote_down') //voting down
 $votes_down = $cur_votes[1]+1;
 $q = "UPDATE entries SET votes_down = $votes_down WHERE id = $id";

$r = mysql_query($q);
if($r) //voting done
 $effectiveVote = getEffectiveVotes($id);
 echo $effectiveVote." votes";
elseif(!$r) //voting failed
 echo "Failed!";
First, we get the requested id and the action. Then the current votes of the id. Remember $cur_votes is an array whose first element is votes_up and the second one is votes_down. Next, then query is determined on the basis of the action requested. Then, we simply update the table and display the value.

Mighty jQuery

Now, we are ready to make good use of jQuery.

 //get the id
 the_id = $(this).attr('id');
 // show the spinner
 $(this).parent().html("<img src='images/spinner.gif'/>");
 //fadeout the vote-count 
 //the main ajax request
   type: "POST",
   data: "action=vote_up&id="+$(this).attr("id"),
   url: "votes.php",
   success: function(msg)
    //fadein the vote count
    //remove the spinner
When 'Vote up' link is clicked, first we get the unique id of the link. Then we show a cool ajax loader in place of the links. Also we fade out the vote count. Next, we make an ajax call and when the response from the script is received, we display the response with a fade-in effect and remove the spinner.

Similarly for vote down link we would have:
 //get the id
 the_id = $(this).attr('id');
 // show the spinner
 $(this).parent().html("<img src='images/spinner.gif'/>");
 //the main ajax request
   type: "POST",
   data: "action=vote_down&id="+$(this).attr("id"),
   url: "votes.php",
   success: function(msg)
All these javascript code goes inside a <script type='text/javascript'> </script> tags of course.
Tip: If you are looking for a more secure, easy to integrate and customise, free solution, you might like to check the open-source alternative Pulse Lite.
That's all!


This is a very simple mock-up of how things work. Security has not been an issue here. More stress on security should be given in production level applications. Also, proper use of session handling would make it more secure.


MacSkolan has implemented this technique with his own modifications at Spotylist.com. He has also offered the source code here. Do check them! My best wishes for MacSkolan's new start-up!

Labels: , ,

blog comments powered by Disqus
Thank you! I will test it for my phone catalog CMS!
Blogger MacSkolan said...
Love the tutorial!
Just implemented the code on a "share spotify playlists" site..
Even though I know almost zero programming I managed to get the features I needed without a lot of fuss. Great work!
Blogger Abhisek said...
@MacSkolan, glad you liked it. Just a word of caution: before you put this code into productional use, please make sure you have enough security. As I mentioned in the tutorial, it has enough loopholes and it is just a demo of how to get things work for you.
Blogger Donovan said...
I like the look of this. Without wanting to be a total nuisance, would it be possible to point out a couple of approaches people should take regarding security before using this in production?
Blogger Abhisek said...
@Donovan, that would be a lot of things to say. You can read some articles there in NetTuts.com. They have some fantastic articles on security.
Blogger mordecai174 said...
great tutorial! ive been looking for something like this for friggen ever.
Blogger tokyoahead said...
It would be great to have some code that sets a cookie which can be used to prevent people to vote 100 times on the same thing. I know its not perfect security, but it would help.

Also, if you want to have a rating 1-6 instead of simply yes/no votes, one can store only Number_of_votes and average and then use the formula:

new_average =((average * no_of_votes) + new_vote) / (1 + no_of_votes)
Blogger MacSkolan said...
We added a cookie thingy for http://spotylist.com
Source code for spotylist:

kind of messy with loads of our custom stuff but I hope it might help.

Philip Harrison
Blogger MacSkolan said...
Damn permissions…
This should work (same as the above source link): http://b4.s3.p.quickshareit.com/files/source2e631.zip
Blogger Abhisek said...
@MacSkolan, could not have been better! Check out the update! ;)

@tokyoahead, what an idea! thanks.
Blogger John said...
Hi... great tutorial. I am having problems, though.

Does the javascript need to be on a document called "jquery1.3.pack.js?"

Blogger Abhisek said...
@John, the javascript codes should go inside
<script type='text/javascript'>
/** Put js codes here **/
Download the source codes. It's all done for you! :)
Blogger john.williams said...

Thanks, I downloaded it and got it to work.

Just a little heads up on the "mighty jQuery"; instead of having:


Wouldn't it be better to utilize jQuerys most awesome feature, chaining?

Blogger Abhisek said...
Point! That would be nice. Thanks :)
Blogger John said...
Hi Abhisek,

How do get Javascript to make the HTML table cell below into a vote up button (equivalent to the thumbs-up icon you use in the tutorial)?

"print "td" .Vote. "/td";"


Blogger Dipesh said...
instead of putting database in image, can you please put it as a code so we can see it. i don't understand how to put those data into that specific database.
in phpmyadmin > first create a db, > enter the db, and in that db > create a table named "entries" (at the bottom there is a field add table and a button "Go")

> enter the table "entries" (at the left navigation underneeth the dbname)
> navigate to structure, in there > add the fields defined in the top of this article,
(at the bottom you wil se: add [1] row (•) at the end of the table, choose 5 insted of 1 and press "Go") > define the rows as shown in the picture and press "Save". > you should be set :)
Blogger Dipesh said...
i still cannot figure it out.
Blogger Abhisek said...
Hi Dipesh!
This is the code to create the table:
CREATE TABLE `entries` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`link` varchar(255) NOT NULL,
`votes_up` int(11) NOT NULL default '0',
`votes_down` int(11) NOT NULL default '0',

And this is the code to insert data inside the table:
INSERT INTO `reddit_votes` (`id`, `title`, `link`, `votes_up`, `votes_down`) VALUES
(1, 'Google', 'http://google.com', 14, 5),
(2, 'Yahoo!', 'http://yahoo.com', 13, 3),
(3, 'Microsoft', 'http://microsoft.com', 11, 10),
(4, 'Colorsonic concept MP3 player turns your tunes into groovy colors', 'http://www.engadget.com/2009/01/30/colorsonic-concept-mp3-player-turns-your-tunes-into-groovy-color/', 7, 5),
(5, 'Sprite Gets More Sprite', 'http://www.underconsideration.com/brandnew/archives/sprite_gets_more_sprite.php#432476', 20, 5);
Blogger Dipesh said...
abhisek thnkx dude.
Blogger Dipesh said...
abhisek bro is it possible to interate comment script within this code?

Blogger egliddon said...
Hi, this code is fantastic! Very helpful.

Is there a way to present the divs in the order of vote count? (highest at the top)?

And how do i make the links open in a new window?

Thanks again

Blogger Dana said...
getting a crc error when trying to unrar tht rar file... using PowerArchiver
Blogger Dana said...
oops, 7zip fixed that, thanks again
Blogger Bill said...
I'm with egliddon. any way to reorder the DIVs to reflect the vote count?
This comment has been removed by the author.
@egliddon and @bill, have not thought about that. but a simple function can create stack of entries arranged according to descending (or ascending) order of vote counts. in pseudo language,

function name(){
get entries;
foreach entry{
count vote;
an_array = entries according to vote count;
return an_array;

And then work with an_array. I guess it will do the work...
Did I make myself clear anyway?
Blogger John said...
Hi Abhishek,

Great code, very helpful. I can't get it to work with the table below, however. Any idea what I'm doing wrong?


$find = strip_tags($find);
$find = trim ($find);

$result=mysql_query("SHOW TABLES FROM sand2 LIKE '%$find%'")
or die(mysql_error());

print "*p class=\"topic\"*$table[0]*/p*\n";
$r=mysql_query("SELECT * FROM `$table[0]`");

print "*table class=\"navbar\"*\n";

$effective_vote = $row['votes_up'] - $row['votes_down'];

print "*tr*";

print "*td*".'*a href="http://'.$row['site'].'" class="links2"*'.$row['site'].'*/a*'."*/td*";
print "*td class='votes'*".'*span class="votes_count" id="votes_count'.$row['id'].'"*'.number_format($effective_vote).'*/span*'."*/td*";
print "*td class='ballot'*".'*span class="button" id="button'.$row['id'].'"*'.'*a href="javascript:;" class="cell1" id="'.$row['id'].'"*'.Vote.'*/a*'.'*/span*'."*/td*";
print "*/tr*\n";
print "*/table*\n";
print "None found";
Blogger John said...
By the way, in my last comment, I replaced all < and > with *. I had to do this so Blogger would publish my comment.

Blogger Abhisek said...
@John, Can't find any issue. What's the error you are getting? Have you tried debugging?
Blogger John said...
Hi Abhisek,

Someone on a forum helped me solve the problem.

I had a mistake in my jQuery.

Thanks for looking, though.

Blogger Abhisek said...
@john, glad you found solution. btw, you can add < or > by replacing & with &amp; i.e. put > as &amp;gt; and < as &amp;lt; ;)
Blogger Abhisek said...
@john, glad you found solution. btw, you can add < or > by replacing & with &amp; i.e. put > as &gt; and < as &lt;
Blogger Concious Index said...
This comment has been removed by a blog administrator.
Blogger Shin Okada said...
@Abhisek: Regarding security at the top of this comments, you gave us a link to Nettuts.

I try to find articles related to the security, but I am not able to find one.

Do you have any articles to recommend reading?

Thanks for your article. I like it.
Blogger david said...
Excellent article! It is complete, and serves as a superb reference on a simple ajax app with jquery, in addition to actually being a working, useful widget. I modified what you have here for use in a Python (pylons) app - and it was easy as pie. Thank you!
Blogger Amits said...
Problem (with the update source) :

Warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource in D:\wamp\www\updownvote\index.php on line 130

The line :
while($row = mysql_fetch_assoc($r)) {

What is the problem?
Blogger Mister Gingle said...
Hi, great tutorial but I have a problem:

if you vote very quickly for 2 items, the first one wont stop spinning!

the solution I tried is to add the variable async: false in the ajax call and it works because it blocks simulaneous calls.

But with it, the spinner doesnt appear on browsers except with firefox?

could you help me?
Blogger Abhisek said...
@Shin Okada, we need a table called "votes_cast" (or whatever name you prefer). It should have 3 columns: uid (unique id), user_id (id of the user) and entry_id (id of the entry). For each vote cast, this table needs to be updated along with the entry_id and the id of the user who is voting. Next, when voting we should check whether the user has already voted. In case, he has, we warn him. That's the maximum security you can have.

@David, my pleasure!

@Amits, I guess there's something wrong with fetching results from the database. Please check it. Also, download the source code and see what's wrong with your code.

@Mister Gingle, what's your specs? it should work on FF2+, IE6+, and probably safari.
Blogger Mister Gingle said...
I have firefox 3.5! your application is working fine except when we click quickly on 2 diffent votes!

when I set async to false, it's working fine on firefox but not on chrome 2 or ie7/8... (the spinner doesn't appear, but the voting process is fine).

do have any idea?
Blogger inetsolution said...
Its highly informative. I would be visiting your blog hereafter regularly to gather valuable information.

content management system company chennai
OpenID iggz83 said...
What method can you use to save the vote so they can't just refresh the page and vote again? Would that involve cookies or sessions? Sorry I'm new with this stuff.

Blogger Abhisek said...
@iggz83, I have already discussed in the comments. We need a table called "votes_cast" (or whatever name you prefer). It should have 3 columns: uid (unique id), user_id (id of the user) and entry_id (id of the entry). For each vote cast, this table needs to be updated along with the entry_id and the id of the user who is voting. Next, whenever a vote is cast, we should check whether the user_id/entry_id pair already exists in this table. In case, it does, it stop the user from voting. That's the maximum security you can have and it doesn't depend on sessions which is pretty crappy and lame.
Blogger Zahid Mahmood said...
Great blog and nice post I am trying to maintain a list of cute blogs. Thanks for nice collection of posts. I’m going to browse through these. it’s nice I can come and read your blog.
ccna ccent
Blogger AMIT said...
All this programming and coding is very hard to do.Thanks for sharing.

Connecticut Drug Rehab
Blogger Wemelon said...
Those who don't want infinite vote, add to votes.php:
****BEFORE function getAllVotes($id)
function cookieSet($name) {
setcookie($name, "rate", time()+3600*24);
****ADD BEFORE if($action=='vote_up')
if(isset($_COOKIE["$id"])) { echo "You've already voted"; } else {
****ADD AFTER echo "Failed!"; }