Grow your CSS skills. Land your dream job.

How To Design and Create a PHP Powered Poll

Published by Chris Coyier

Polls are fun! They can help engage the readers of your site and give both you and the poll-taker valuable information. Let's walk through building a poll from scratch. All the way from the Photoshop design to the PHP / MySQL that powers it. Here is what we will build:

View Demo   Download Files

1. Design the Background in Photoshop

Create a brand new Photoshop document. In mine, I have filled the background with a dark blue (#233743) and have it sized at 700x700px.

Then create a brand new layer (press the little page icon in the layers palette) on top of your background layer. Select the gradient tool (sub tool of the paint bucket tool). Make sure you have the gradient tool set to Foreground to Transparent, radial, and full opacity like this:

In mine, I used a slightly lighter blue color (#364c5a) and drug out a gradient from near the top middle. The gradient can go "off" the top, but make sure it doesn't go off either side or the bottom. The idea here is that we are going to center this over a background of a matching blue, so we don't want the gradient to end abruptly. The reason we are making this on it's own layer is so we can nudge it around without having to re-do it.

Now let's add the fun "POLL!" text at the top. Here I used the font Agenda Black (one of my new favs) in an even lighter shade of blue (#e3f1fa). I free-transformed it (Command-T) in order to rotate it a bit and then gave it a slight drop shadow in the layer styles.

Now the graphic is ready to be saved out. You can "Save for Web & Devices" from the file menu. Use a high quality JPG setting (best for larger images with gradients). Name the file "page-bg.jpg" and save it into an "images" folder inside a directory you will use for this project.

 

2. Building the Page Structure

A poll, in our case, is just a really simple form. Basically a series of radio button inputs and a submit button. Here is what the entire HTML markup looks like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Poll</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<fieldset>
	<legend>What is your JavaScript library of choice?</legend>
	<form action="<?php echo $editFormAction; ?>" id="form1" name="form1" method="POST">
		<label>
			<input type="radio" name="Poll" value="mootools" id="Poll_0" />
			Mootools
		 </label>
		<label>
			<input type="radio" name="Poll" value="prototype" id="Poll_1" />
			Prototype
		</label>
		<label>
			<input type="radio" name="Poll" value="jquery" id="Poll_2" />
			jQuery
		</label>
		<label>
			<input type="radio" name="Poll" value="spry" id="Poll_3" />
			Spry
		</label>
		<label>
			<input type="radio" name="Poll" value="other" id="Poll_4" />
			Other
		</label>
		<input type="submit" name="submit" id="submit" value="Vote" />
		<input type="hidden" name="id" value="form1" />
		<input type="hidden" name="MM_insert" value="form1" />
	</form>
</fieldset>
</body>
</html>

Few things to note in the markup here. I put my form inside a fieldset. Nothing functionality related here, I just like how the fieldset/legend combination looks and gives us a hook for some CSS styling. Also notice how the inputs are inside the label elements. This allows users to click on the words as well as the radio button to select it, which is nice. Also notice the bit of PHP in the action for the form, we'll get to that later.

Here is the CSS:

* { 
	margin: 0; 
	padding: 0; 
}
body { 
	font-size: 62.5%; 
	font-family: Georgia, serif;
	background: url(images/page-bg.jpg) top center no-repeat #233743; 
}
h6 {
	font-size: 1.4em;
	margin-bottom: 15px;
}
a { color: white; }
label, li {
	display: block;
	padding: 5px;
	font-size: 1.4em;
	color: #e3f1fa;
}
fieldset {
	margin: 115px auto;
	width: 400px;
	padding: 8px 15px 15px 15px;
	border: 1px solid white;
	display: block; /* IE 7 Requires This */
	}
	legend {
		padding: 4px 6px 4px 6px;
		border: 1px solid white;
		font-size: 2.0em;
		color: #e3f1fa;
		font-style: italic;
	}
ul { list-style: none; margin-bottom: 15px;}
.results-bar {
	padding: 10px;
	color: white;
	background: url(images/result-bar-bg.png) left center;
	white-space: nowrap;
}
span.total-votes {
	font-size: 2.6em;
}

Notice the styles there at the bottom for things that aren't in our markup yet, those are for the Results page which we'll get to later.

 

3. Create a Database to Store Results

Most hosting packages allow you to create databases on your server. If you don't already know how, you may need to contact them or search around their help areas to find out how to add a new one.

CSS-Tricks is on Media Temple, so there is a handy little tool right in the domain admin area for creating new databases:

Make sure this is a MySQL database. The four things you will need to know are the hostname, the database username, the database "password", and the name of the database.

Now you will need to create a new file in the directory you have started for this project called "conn_vote.php". I have put mine inside a subfolder called "Connections". Here is the PHP:

<?php
# FileName="Connection_php_mysql.htm"
# Type="MYSQL"
# HTTP="true"
$hostname_conn_vote = "localhost";
$database_conn_vote = "your-database-name";
$username_conn_vote = "your-database-username";
$password_conn_vote = "your-database-password";
//$conn_vote = mysql_pconnect($hostname_conn_vote, $username_conn_vote, $password_conn_vote) or trigger_error(mysql_error(),E_USER_ERROR);
$conn_vote = mysql_connect($hostname_conn_vote, $username_conn_vote, $password_conn_vote) or die('Can\'t create connection: '.mysql_error());
mysql_select_db($database_conn_vote, $conn_vote) or die('Can\'t access specified db: '.mysql_error());
?>

Notice the four lines in bold above, those are the four variables for the four things I said you would need to know. Chances are good that your host is going to be localhost, but it isn't always. In the case of Media Temple like me, it's something like this: internal-db.s12345.gridserver.com

Your brand new empty database is going to need a table structure and some fake data to get started. Here is some SQL you can run to get that done:

-- 
-- Table structure for table `poll`
-- 
CREATE TABLE `poll` (
  `id` int(3) NOT NULL auto_increment,
  `question` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) TYPE=MyISAM AUTO_INCREMENT=43 ;
-- 
-- Dumping fake data for table `poll`
-- 
INSERT INTO `poll` VALUES (42, 'jquery');
INSERT INTO `poll` VALUES (41, 'mootools');
INSERT INTO `poll` VALUES (40, 'other');
INSERT INTO `poll` VALUES (39, 'mootools');
INSERT INTO `poll` VALUES (38, 'jquery');
INSERT INTO `poll` VALUES (37, 'mootools');
INSERT INTO `poll` VALUES (36, 'spry');
INSERT INTO `poll` VALUES (35, 'jquery');
INSERT INTO `poll` VALUES (21, 'mootools');
INSERT INTO `poll` VALUES (22, 'other');
INSERT INTO `poll` VALUES (23, 'mootools');
INSERT INTO `poll` VALUES (24, 'mootools');
INSERT INTO `poll` VALUES (25, 'prototype');
INSERT INTO `poll` VALUES (26, 'other');
INSERT INTO `poll` VALUES (27, 'mootools');
INSERT INTO `poll` VALUES (28, 'spry');
INSERT INTO `poll` VALUES (29, 'jquery');
INSERT INTO `poll` VALUES (30, 'mootools');
INSERT INTO `poll` VALUES (31, 'prototype');
INSERT INTO `poll` VALUES (32, 'mootools');
INSERT INTO `poll` VALUES (33, 'mootools');
INSERT INTO `poll` VALUES (34, 'mootools');

Most hosts also give you access to phpMyAdmin for running stuff like this. Make sure you are on your new database and then you can paste in that SQL and run it.

 

4. Make it Work with PHP

Now we are ready for some PHP coding magic to make this all work. I can't pretend to understand all this, but the gist of it is that it will take your selected option, turn it into a nice readable and safe string value, and save it as a new entry in that table in our DB.

Huge thanks to Jonathan Feanfor the PHP that powers this thing and David Walsh for helping me work out some kinks and get it up and running on my server.

Insert this PHP code at the top (even before the DOCTYPE) of your poll.php file:

<?php require_once('Connections/conn_vote.php'); ?>
<?php
if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;    
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}

$editFormAction = $_SERVER['PHP_SELF'];
if (isset($_SERVER['QUERY_STRING'])) {
  $editFormAction .= "?" . htmlentities($_SERVER['QUERY_STRING']);
}

if ((isset($_POST["MM_insert"])) && ($_POST["MM_insert"] == "form1")) {
  $insertSQL = sprintf("INSERT INTO poll (id, question) VALUES (%s, %s)",
                       GetSQLValueString($_POST['id'], "int"),
                       GetSQLValueString($_POST['Poll'], "text"));

  mysql_select_db($database_conn_vote, $conn_vote);
  $Result1 = mysql_query($insertSQL, $conn_vote) or die(mysql_error());

  $insertGoTo = "results.php";
  if (isset($_SERVER['QUERY_STRING'])) {
    $insertGoTo .= (strpos($insertGoTo, '?')) ? "&" : "?";
    $insertGoTo .= $_SERVER['QUERY_STRING'];
  }
  header(sprintf("Location: %s", $insertGoTo));
}

$colname_rs_vote = "-1";
if (isset($_GET['recordID'])) {
  $colname_rs_vote = $_GET['recordID'];
}
mysql_select_db($database_conn_vote, $conn_vote);
$query_rs_vote = sprintf("SELECT * FROM poll WHERE id = %s", GetSQLValueString($colname_rs_vote, "int"));
$rs_vote = mysql_query($query_rs_vote, $conn_vote) or die(mysql_error());
$row_rs_vote = mysql_fetch_assoc($rs_vote);
$totalRows_rs_vote = mysql_num_rows($rs_vote);
?>

And this PHP at the very end of the poll.php file (even after the <html>):

<?php
mysql_free_result($rs_vote);
?>

5. Create a Results Page

So our polls is up and working and successfully gathering votes, but the results page is the true payoff! Not only is this the fun part, but our PHP function auto-re-directs to a "results.php" page after you press the vote button, so we kinda have to build one =)

This time I will give you all the markup and the PHP together in one big lump:

<?php require_once('Connections/conn_vote.php'); ?>
<?php
if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;    
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}

mysql_select_db($database_conn_vote, $conn_vote);
$query_rs_vote = "SELECT * FROM poll";
$rs_vote = mysql_query($query_rs_vote, $conn_vote) or die(mysql_error());
$row_rs_vote = mysql_fetch_assoc($rs_vote);
$totalRows_rs_vote = mysql_num_rows($rs_vote);

$resultQuestion1 = mysql_query("SELECT * FROM poll WHERE question='mootools'");
$num_rowsQuestion1 = mysql_num_rows($resultQuestion1);

$resultQuestion2 = mysql_query("SELECT * FROM poll WHERE question='prototype'");
$num_rowsQuestion2 = mysql_num_rows($resultQuestion2);

$resultQuestion3 = mysql_query("SELECT * FROM poll WHERE question='jquery'");
$num_rowsQuestion3 = mysql_num_rows($resultQuestion3);

$resultQuestion4 = mysql_query("SELECT * FROM poll WHERE question='spry'");
$num_rowsQuestion4 = mysql_num_rows($resultQuestion4);

$resultQuestion5 = mysql_query("SELECT * FROM poll WHERE question='other'");
$num_rowsQuestion5 = mysql_num_rows($resultQuestion5);

$percentQuestion1 = ($num_rowsQuestion1 / $totalRows_rs_vote)*100;
$percentQuestion2 = ($num_rowsQuestion2 / $totalRows_rs_vote)*100;
$percentQuestion3 = ($num_rowsQuestion3 / $totalRows_rs_vote)*100;
$percentQuestion4 = ($num_rowsQuestion4 / $totalRows_rs_vote)*100;
$percentQuestion5 = ($num_rowsQuestion5 / $totalRows_rs_vote)*100;

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>Results</title>
	<link href="style.css" rel="stylesheet" type="text/css" />
</head>

<body>
	<fieldset>
	
		<legend>Results</legend>
		
		<ul>
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion1 ?></span> Mootools
				<br />
				<div class="results-bar" style="width: <?php echo round($percentQuestion1,2); ?>%;">
					 <?php echo round($percentQuestion1,2); ?>%
				</div>
			</li>
			
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion2 ?></span> Prototype
				<div class="results-bar" style="width: <?php echo round($percentQuestion2,2); ?>%;">
					 <?php echo round($percentQuestion2,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion3 ?></span> jQuery
				<div class="results-bar" style="width: <?php echo round($percentQuestion3,2); ?>%;">
					 <?php echo round($percentQuestion3,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion4 ?></span> Spry
				<div class="results-bar" style="width: <?php echo round($percentQuestion4,2); ?>%;">
					 <?php echo round($percentQuestion4,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion5 ?></span> Other
				<div class="results-bar" style="width: <?php echo round($percentQuestion5,2); ?>%;">
					 <?php echo round($percentQuestion5,2); ?>%
				</div>
			</li>
		</ul>
	
		<h6>Total votes: <?php echo $totalRows_rs_vote ?></h6>
		
		<a href="poll.php">Back to Voting</a>
	
	</fieldset>
	
</body>
</html>

<?php
mysql_free_result($rs_vote);
?>

Notice there is a little math being done in the PHP which calculates the percentage of the total votes for each poll option. Not only is this good information, but we can use that percentage to set the width of a bar to add some visual flair to our results. Each poll option has it's own list item where the total votes for that item is displayed along with a "results-bar", who's width is determined by setting an inline width value to the percentage calculated. Back in our CSS, we already have that div styled up with a subtle patterned background image.

View Demo   Download Files

(Remember: this is requires a server running PHP and a MySQL database so you'll need to follow the steps above to have it work on your own server. Photoshop file included.)

Comments

  1. Most awesome. This would work too on people’s hosts who are only allowed one database by just adding the table information right? Would this also work for multiple polls? If I missed this I apologize. :) I <3 PHP so much!

    • Mel Florance
      Permalink to comment#

      Hello,

      isn’t it too much overkill ?

      Like this one :
      “SELECT * FROM poll”… and this 5 times !!

      I think something like that is better :
      $questions = ['question1,'question2', 'question3'];
      mysql_query(‘SELECT poll.field1, poll.field2… FROM poll WHERE poll.question IN (‘.implode(‘, ‘, $questions).’);’);

      :)

  2. @Lindsey: Sure, you could add the table to an existing DB if you wanted, I just didn’t wanna go advocating messing with already existing and important databases =). I know I sure wasn’t gonna test it out on my own WordPress database!

    And yeah, I think this would work for multiple polls as long as the answers were all different. I didn’t do the PHP for this, so I’m not intimately familiar, but it looks like the answers are being stored in the table as literally a string of the answer value, not something unique like an ID#.

  3. Excellent Works, i’ve already done those kind of pools for others websites.
    And i can say that this code is very well.
    Very professionnal.
    Maybe, the little next step is to call an ajax function to display it (result.php) on the same page, it’s very easy…

    Good Work.
    Bye, Laurent.

  4. Donna

    This is a great start and I love the styling (i.e. the clean-ness in the way it displays and performs). For me, the next step would be to de-couple the data from the source code. I would rather see the poll answers/questions come from the database and maintain a vote count field in the database. The way it is now, a new entry gets added to the database for each vote so you could end up with 10,000 entries or rows in the database table if there are 10,000 votes.

    On the other hand, this is simple which is nice. Thanks for making this available.

  5. @Laurent: Good idea on having the results page load via AJAX. I think I’ll work on that and I’ll post something if I get it.

    @Donna: You are right, it probably would be best for a simple poll like this store and increment a total in the DB instead of every single entry. Doing it this way though, you could add something like “Name” or “Email” and so the poll could also be gathering information for you. Then you would want to store each entry separately…

  6. Donna

    Good Point Chris. You’re right that it could come in handy to gather other information in the poll.

    Thanks again for all your work on this great site. This is my favorite site to check out each day — I always learn something new and useful!

  7. Wow nice post Chris! You did a great job explaining the database and php made it real easy. Thanks!

  8. This is a very nice tutorial as it covers a lot of bases. I do have a question though, what if you want to make it ‘editable’. I.E. you want to allow people to create their own polls. Is there an editPoll script/php page addendum to this? The reason i ask is that polls are nice, but they are best when others can simply click and enter variables and submit to one and all.

    I’m really super new to this whole world of PHP/Coding and stuff like that (soemthing i’m doing while dealing with cancer) so if my questions are dumb, I apologize. I’m just trying to figure stuff out so i can leave a little something behind for my friend should my health go really south really quickly.

    thanks for any help or guidance – and once again i like your tutorial – it’s very spiffy and i feel I learned a lot :)
    -Chez

  9. Hi Again
    So uhm…. In scrolling i now realize that I missed one of Donna’s uber-insightful comments. I agree with her, when she says ‘I would rather see the poll answers/questions come from the database and maintain a vote count field in the database. The way it is now, a new entry gets added to the database for each vote so you could end up with 10,000 entries or rows in the database table if there are 10,000 votes.’

    Although for folks like me, if i were to get 20 votes i’d be happy and i think that’s how most polls when deployed work this way – Yahoo Groups offer polls and so do a lot of other boards and such and i think that if they are factored in, their silly polls (which are as silly as mine would be so i’m not talking down to them when i say this). I like the idea of the database containing everything and managing the data – i also like the idea of it gathering info for you like IPs so that somone can only vote once so information isn’t distorted. Also the gathering of e-mails and other things is pretty cool too.

    Anything thoughts, hints or suggestions?
    Also is this like a daily blog i’ve stumbled on to and not realized?

    Thanks for the great poll example!
    hugs
    -Chez

  10. First, i don’t know if this code is ready, because i have no competence with php.

    Second, i will test it for my own website to get more information about a special topic.

    Thank you for your work.

    Ralph

  11. Permalink to comment#

    Thanks you helped me a lot now my poll look better.

  12. John
    Permalink to comment#

    Cool Tutorial… What about an enhancement that shows how to keep voters from voting more than once? Thanks again!

  13. Permalink to comment#

    Very nice tut. I am hoping to have something up next week. TY Chris for making this post so that people like me can understand it.

  14. Carlos
    Permalink to comment#

    Is there a way to keep people from voting more than once on these polls.

  15. adi

    “CREATE TABLE poll (
    id int( 3 ) NOT NULL AUTO_INCREMENT ,
    question varchar( 200 ) default NULL ,
    PRIMARY KEY ( id )
    ) TYPE : MYISAM AUTO_INCREMENT :43;

    MySQL menyatakan: Dokumentasi
    #1064 – You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘TYPE:MyISAM AUTO_INCREMENT:43′ at line 5 ”

    please help me to correct that problem…..

  16. Hi, nice poll here :D
    but i want to create something like this

    “User should login (there should be with session), then select the poll for candidates, then logout, and another user login again so on. ”

    User -> login -> votes -> logout -> and so on.
    can you help me with tutorial ? :)

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".