Sort & Vote - A jQuery Poll
Today we are going to make a draggable sort & vote poll, that will enable our website visitors to choose their favorite tutorial from the site. After they vote, a friendly CSS chart will show them how the tutorials are ranked and the total number of voters.
To achieve this, we will be using jQuery, jQuery UI, PHP, CSS & MySQL.
You can use the code I provide here to make your own versions and mash-ups.
Note: This tutorial is quite old and doesn't work in PHP7 and above. We are keeping it online only as a reference.
Step 1 - XHTML
To make a better understanding of these steps, it would be nice to have the download files nearby and open, so you can trace where the code I explain here originates from.
We start with the XHTML markup. A big deal of this code is programatically added by PHP. Below is the section extracted from demo.php:
demo.php
<div id="main"> <?php /* ... */ ?> <div class="clear"></div> <!-- The form below is not directly available to the user --> <form action="?results" id="sform" method="post"> <input name="sortdata" id="sortdata" type="hidden" value="" /> </form>
Worth noting is the sform form. It contains a single, hidden textfield - sortdata. It gets filled by jQuery in the moment we hit the poll submit button, and holds a string representing the ordering and unique IDs of the tutorials we are sorting. We will get back to this in a moment.
Maybe more interesting here is the PHP part on lines 3-5. It contains the code that handles the generation of the sortable list items and the creation of the chart. I have included below only the XHTML markup that is used to build the list. We will take a closer look at the rest of this code in the PHP section of this tutorial.
<li id="<?php echo $row['id']?>"> <div class="tut"> <div class="tut-img"> <img src="<?php echo $row['img']?>" width="100" height="100" alt="<?php echo $row['title']?>" /> <div class="drag-label"></div> </div> <div class="tut-title"> <a href="<?php echo $row['url']?>" target="_blank" title="Open it in a new window!"><?php echo $row['title']?></a> </div> <div class="tut-description"><?php echo $row['description']?></div> <div class="clear"></div> </div> </li>
This code is positioned inside a while loop, that outputs it for each list item. And because we have included a few echo-s, this allows us to insert dynamic data, in this case titles and urls pulled from the database.
Now lets dig a little deeper into the CSS styles.
Step 2 - CSS
In the code below, you can see how we have styled the XHTML we generated earlier. Because of the length of the code, I've only included what is directly used by the poll. You can view the rest of the code, which styles the demo page itself, in demo.css.
demo.css - Part 1
.tut-title{ font-size:20px; font-weight:bold; } .tut-description{ color:#DDDDDD; font-family:Arial,Helvetica,sans-serif; font-size:11px; padding-top:5px; } .tut-img{ border:1px solid white; float:left; margin:0 15px 0 0; width:100px; height:100px; overflow:hidden; /* CSS3 Box Shadow */ -moz-box-shadow:2px 2px 3px #333333; -webkit-box-shadow:2px 2px 3px #333333; box-shadow:2px 2px 3px #333333; cursor:n-resize; position:relative; } .drag-label{ /* The DRAG label that scrolls into visibility on hover */ background:url(img/label_small.png) no-repeat; width:71px; height:25px; position:relative; margin-left:25px; } a.button{ /* The pretty buttons on the bottom are actually hyperlinks.. */ color:#434343 !important; display:block; float:left; font-size:10px; font-weight:bold; height:23px; margin:10px; padding:12px 10px 0 12px; position:relative; text-shadow:none; text-transform:uppercase; /* This is the left part of the button background image */ background:transparent url(img/button_gray_bg.png) no-repeat; } a.button:hover{ text-decoration:none !important; background-position:bottom left; } a.button:active{ /* Offsetting the text 1px to the bottom on mouse-click*/ padding-top:13px; height:22px; } a.button span{ /* This span holds the right part of the button backgound */ background:transparent url(img/button_gray_bg.png) no-repeat right top; height:35px; position:absolute; right:-2px; top:0; width:10px; display:block; } a.button:hover span{ background-position:bottom right; }
There are some interesting techniques and properties used in the code above. One of these is the special CSS3 property box-shadow property, that adds a shadow below each thumbnail.
In the illustration below, you can see how I created the fancy submit / result buttons.
demo.css - Part 2
.button-holder{ padding-left:107px; } ul.sort{ /* This UL gets converted to a sortable by jQuery */ font-family:"Myriad Pro",Arial,Helvetica,sans-serif; font-size:20px; } ul.sort li{ margin:25px 50px 25px 0; height:102px; list-style:none; } .chart{ /* Styling the chart container */ background:#002A3C; border:1px solid #005A7F; height:300px; width:550px; } .bar{ /* Each bar in the chart is a div. Colors and width are assigned later */ height:17px; margin:15px; overflow:hidden; padding:15px 10px 10px; text-shadow:none; white-space:nowrap; } .bar a, .bar a:visited{ color:white; font-size:12px; } .tot-votes{ float:right; font-size:10px; font-weight:bold; position:relative; right:50px; text-transform:uppercase; top:18px; }
In this part of the code we have the classes that style the chart. As you can see, at this point we have not assigned colors nor widths to the bars, mainly because those two styles are generated according the number of votes each entry received. We will get back to this in a moment.
Step 3 - PHP
PHP generates the sortable list elements, communicates with the database and outputs the chart.
Below you can see the code we mentioned earlier. It firstly fetches all the objects from the database, and secondly outputs the sortable list.
demo.php
// Checking whether the user has voted today: $voted=false; $vcheck=mysql_query(" SELECT 1 FROM sort_votes WHERE ip='".$_SERVER['REMOTE_ADDR']."' AND date_submit=CURDATE()"); if(mysql_num_rows($vcheck)==1) $voted=true; // If we are not on the data.php?results page: if(!array_key_exists('results',$_GET)) { echo '<ul class="sort">'; // Showing the tutorials by random $res = mysql_query("SELECT * FROM sort_objects ORDER BY RAND()"); while($row=mysql_fetch_assoc($res)) {?> <li id="<?php echo $row['id']?>"> <div class="tut"> <div class="tut-img"> <img src="<?php echo $row['img']?>" width="100" height="100" alt="<?php echo $row['title']?>" /> <div class="drag-label"></div> </div> <div class="tut-title"> <a href="<?php echo $row['url']?>" target="_blank" title="Open it in a new window!"><?php echo $row['title']?></a> </div> <div class="tut-description"><?php echo $row['description']?></div> <div class="clear"></div> </div> </li> <?php } ?> </ul> <div class="button-holder"> <?php if(!$voted):?><a href="" id="submitPoll" class="button">Submit Poll<span></span></a><?php endif;?> <a href="?results" class="button">View The Results<span></span></a> </div> <?php } else require "results.php"; // The above require saves us from having to create another separate page
Once the user has rearranged the entries and submitted the form, results.php is dynamically included in the page with the use of the require function.
results.php
// If the poll has been submitted: if($_POST['sortdata']) { // The data arrives as a comma-separated string, // so we extract each post ids: $data=explode(',',$_POST['sortdata']); // Getting the number of objects list($tot_objects) = mysql_fetch_array(mysql_query("SELECT COUNT(*) FROM sort_objects")); if(count($data)!=$tot_objects) die("Wrong data!"); foreach($data as $k=>$v) { // Building the sql query: $str[]='('.(int)$v.','.($tot_objects-$k).')'; } $str = 'VALUES'.join(',',$str); // This will limit voting to once a day per IP: mysql_query(" INSERT INTO `sort_votes` (ip,date_submit,dt_submit) VALUES ('".$_SERVER['REMOTE_ADDR']."',NOW(),NOW())"); // If the user has not voted before today: if(mysql_affected_rows($link)==1) { mysql_query(' INSERT INTO `sort_objects` (id,votes) '.$str.' ON DUPLICATE KEY UPDATE votes = votes+VALUES(votes)'); } } // Selecting the sample tutorials and ordering // them by the votes each of them received: $res = mysql_query("SELECT * FROM sort_objects ORDER BY votes DESC"); $maxVote=0; $bars=array(); while($row=mysql_fetch_assoc($res)) { $bars[]=$row; // Storing the max vote, so we can scale the bars of the chart: if($row['votes']>$maxVote) $maxVote = $row['votes']; } $barstr=''; // The colors of the bars: $colors=array('#ff9900','#66cc00','#3399cc','#dd0000','#800080'); foreach($bars as $k=>$v) { // Buildling the bar string: $barstr.=' <div class="bar" style="width:'.max((int)(($v['votes']/$maxVote)*450),100).'px;background:'.$colors[$k].'"> <a href="'.$v['url'].'" title="'.$v['title'].'">'.$v['short'].'</a> </div>'; } // The total number of votes cast in the poll: list($totVotes) = mysql_fetch_array(mysql_query("SELECT COUNT(*) FROM sort_votes"));
After the poll is submitted, this script extracts the ids of the sortable entries and adds 1-5 votes to each one, depending on its position. Later the new votes are added to the sort_objects table.
It is important that users are allowed to vote only once per day. This is why we insert a new row in the sort_votes table every time a user submits the form.
The special thing about this table is that it has a unique key defined on the ip and date field. This means that MySQL will throw an error if we try to insert a duplicate row in the table and thus limiting the votes per IP.
Another thing mentioned earlier is how we generate the bars. You can see that we assign two CSS properties in the style attribute of the bars - a width and a background color. They are dynamically assigned according to the number of votes, as you can see from line 59.
Later the generated $barstr variable is outputted on the page and the chart is complete.
Step 4 - jQuery
Before being able to use jQuery, we need to include all the needed files. In this tutorial, we are using both the jQuery library and jQuery UI (for the sortable list), so we include them both, as well as our own custom script.js file and the stylesheet.
demo.php
<link rel="stylesheet" type="text/css" href="demo.css" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script> <script type="text/javascript" src="script.js"></script>
OK, but what is this sortable list I've been talking about?
This is a special jQuery UI component (the entire jQuery UI is focused on user interfaces and is an addition to the main jQuery library), that allows the developer to convert any ordered or unordered list (OL or UL) into a draggable and sortable user interface component. The library also provides methods to get the order of the elements in JavaScript, which you can later use in your application.
Pretty neat actually.
Now lets see how this works.
script.js
$(document).ready(function(){ // Executed once all the page elements are loaded // Convert the UL with all the tutorials into a sortable list: $("ul.sort").sortable({ handle : '.tut-img', // We provide the thumbnails as drag handles axis:'y', containment: 'document', // The elements cannot be dragged outside the document opacity: 0.6 }); // The hover method takes a mouseover and a mouseout function: $(".tut").hover( function(){ $(this).find('.drag-label').stop().animate({marginTop:'-25px'},'fast'); }, function(){ $(this).find('.drag-label').stop().animate({marginTop:'0'},'fast'); } ); // Binding an action to the submitPoll button: $('#submitPoll').click(function(e){ // We then turn the sortable into a comma-separated string // and assign it to the sortdata hidden form field: $('#sortdata').val($('ul.sort').sortable('toArray').join(',')); // After this we submit the form: $('#sform').submit(); // Preventing the default action triggered by clicking on the link e.preventDefault(); }); });
Yep, it is that simple - just use the sortable() method with some options and you are done.
Later we just take the sort order of the elements with sortable('toArray'). What this does is to return all the ids of the list elements in the order they are sorted.
When the submitPoll button is pressed, we take the data the aforementioned method, join it into a string, and assign it to the sortdata field in the sform form that is later submitted to results.php.
Step 5 - MySQL
If you plan to run this demo on you own, make sure to create the sort_votes and sort_objects tables in a MySQL database, and later fill in your connection details in connect.php.
You can execute the code from tables.sql in your favorite database manager (e.g. PHPMyAdmin) and the two tables will be created automatically.
With this our dragable sort & vote poll is complete!
Conclusion
Today we used jQuery and the sortable method, made a fancy chart with only a bit of PHP and CSS and demonstrated some interesting database interactions.
You are free to use the resulting code in your own sites. The script can be easily modified to fit almost any needs.
Also be sure to check out our twitter feed - every once and a while we share links to awesome stuff created by the community and inspired by our tutorials.
Bootstrap Studio
The revolutionary web design tool for creating responsive websites and apps.
Learn more
Awesome tut man!!
Your blog is been turning one of my favourites.
Wow..!! I liked it very much ..
I'm going to use it on my site...
Simply stunning and also very comprehensive. Very nice tutorial.
I want to use this and am rather a novice on MySQL and wanted to see if you can help me with one thing.
The result page is what I want to edit.
If an object has not been voted on, I would like to make its bar color black so that only the bar color of object user voted on would have its color. Any idea how I would do that?
Thanks in advance!
Way to think outside of the box on this one. Very nice.
A valuable tutorial! I'll read and learn a lot.
A very detailed and informative tutorial. I loved this new approach to polling. I have compiled such tutorials and scripts at 11 PHP Voting Scripts and Tutorials I hope it would be helpful to many, :-)
Great tutoria Martin I’m going to have to give this a go.