Voting using YUI sliders and AJAX

Some weeks back at work we had a political debate featuring the leaders of the 8 biggest political parties in Norway. The event was streamed live on the net via VGTV (our internet “TV channel”).

We wanted to involve the viewers so we made it possible for them to vote for their favourite politicians while the debate lasted. We wanted to see if some of the things the politicians said would alter the viewers’ votes. To be able to do this without interrupting the video stream we had to do some AJAX trickery. I was in charge of the technical solution and since I haven’t been blogging too much about technical stuff lately I thought I could write a post about some of the stuff I used for the voting.

First I’ll include a screenshot of how the web page showing the video looked like.

Debate snapshot

The stream was shown where the nice black box is and the voting was done using the two thumbs to the right (over and below the images of the politicians). The thumbs are actually the “handle” on two horizontal sliders that were implemented using the Slider widget of the YAHOO! User Interface library (YUI). The sliders are made using some simple HTML markup and a little bit of JavaScript code. The images between the two sliders are just a simple list with some background images for the elements. The HTML markup that was used to make the voting box looks something like this:

<div id="booth">
  <div id="positive">
    <div id="positiveSlider">
      <div id="positiveSliderThumb"><img src="images/positiveThumb.gif" /></div>
    </div>
  </div>
  <ul id="parties">
    <li id="noParty"> </li>
    <li id="party1"> </li>
    <li id="party2"> </li>
    <li id="party3"> </li>
    <li id="party4"> </li>
    <li id="party5"> </li>
    <li id="party6"> </li>
    <li id="party7"> </li>
    <li id="party8"> </li>
  </ul>
  <div id="negative">
    <div id="negativeSlider">
      <div id="negativeSliderThumb"><img src="images/negativeThumb.gif" /></div>
    </div>
  </div>
</div>

As you can see the sliders are just a div container that contains another container that represents the “handle” of the slider. In this case the handles are images of thumbs. These elements are styled using CSS and the following code is used in a style sheet:

#booth {float: left; background: #f2f2f2 url(images/boothBg.gif) 0 0 no-repeat; width: 435px; height: 453px;}
#positive,
#negative {clear: left; position: relative;}
#positive {background: transparent url(graphs/positiveGraph.png) bottom right no-repeat; height: 180px;}
#negative {background: transparent url(graphs/negativeGraph.png) top right no-repeat; height: 125px;}
#positiveSlider,
#negativeSlider {position: absolute; width: 430px; height: 45px; left: 5px;}
#positiveSlider {bottom: 0px;}
#negativeSlider {top: 0px;}
#positiveSlider:hover,
#negativeSlider:hover {cursor: pointer;}
#positiveSliderThumb,
#negativeSliderThumb {position: absolute;}

#parties {list-style-type: none; padding: 0; margin: 0;}
#parties li {float: left; padding: 0; margin: 0;}

#noParty {width: 51px; height: 57px; background: transparent url(images/noParty.jpg) 0 0 no-repeat;}
#party1 {width: 49px; height: 57px; background: transparent url(images/party1.jpg) 0 0 no-repeat;}
#party2 {width: 47px; height: 57px; background: transparent url(images/party2.jpg) 0 0 no-repeat;}
#party3 {width: 51px; height: 57px; background: transparent url(images/party3.jpg) 0 0 no-repeat;}
#party4 {width: 43px; height: 57px; background: transparent url(images/party4.jpg) 0 0 no-repeat;}
#party5 {width: 52px; height: 57px; background: transparent url(images/party5.jpg) 0 0 no-repeat;}
#party6 {width: 47px; height: 57px; background: transparent url(images/party6.jpg) 0 0 no-repeat;}
#party7 {width: 47px; height: 57px; background: transparent url(images/party7.jpg) 0 0 no-repeat;}
#party8 {width: 48px; height: 57px; background: transparent url(images/party8.jpg) 0 0 no-repeat;}

The reason that I used empty list elements with background images instead of just placing image tags in the list elements was because of a margin below the list that appeared when I used image tags. I could not seem to remove it using CSS, so I used background images instead.

The rest of the CSS code should be pretty self explanatory. The slider containers should have a relative position, and the slider thumbs should be absolute. This is specified in the YUI docs for the slider widget.

The JS code that was used to create the sliders are pretty simple as well:

/* Create the slider object for the positive vote */
var positiveSlider = YAHOO.widget.Slider.getHorizSlider("positiveSlider", "positiveSliderThumb", 0, 390, 48);

/* Run the validatePositiveVote function when the slideEnd event is fired, which happens when a slide is finished */
positiveSlider.subscribe("slideEnd", validatePositiveVote);

To create the slider object you need to specify the slider container as the first argument and the slider thumb container as the second argument. The three last arguments are the left and right boundaries and the width of each “tick” in the slider respectively. If the last argument is skipped the slider will move in a more smooth manner, but in this case I wanted to “attach” the slider thumbs to the images of the politicians.

The sliders also let you run different code when certain events occur. The different events are: “slideStart”, “slideEnd” and “change”.

Since we don’t want the users to place both their votes on the same politician we run some validation functions. The validatePositiveVote function looks something like:

function validatePositiveVote() {
  var currentPositiveVote = positiveSlider.getValue();
  var currentNegativeVote = negativeSlider.getValue();

  if (currentPositiveVote == currentNegativeVote) {
    negativeSlider.setValue(0);
  }
}

As we can see we simply fetch both the sliders’ values and if they are the same we will move the slider that was already placed on the politician back to start. positiveSlider and negativeSlider are in this case global variables.

To be able to register the votes I made a function that would see if the user had changed his votes and if so, post the votes to a PHP script that would register the votes in a database. To make a function run periodically I used the windows.setInterval() JS function. The function takes two parameters. The first parameter is the function we want to run and the second is the interval in microseconds.

/* Update the votes every 10 seconds */
window.setInterval(updateVotes, 10000);

The updateVotes function uses the Connection Manager utility of YUI to send a POST request to the PHP script. The function looks something like this:

function updateVotes() {
  var positiveVote = positiveSlider.getValue();
  var callback = {
    success: function(o) {},
    failure: function(o) {},
    argument: null
  };

  if (positiveVote != lastPositiveVote) {
    lastPositiveVote = positiveVote;

    YAHOO.util.Connect.asyncRequest('POST', 'registerVote.php', callback, "vote=" + positiveVote + "&positive=1&clientKey=" + clientKey);
  }
}

First we just fetch the x coordinate of the positive slider and post it to the registerVote.php script if the vote is different from the last vote. The clientKey that is sent in the POST is just a unique key that each client (browser) gets when first visiting the site.

If you look at the screenshot above you can also see some graphs. These graphs are generated in a php script that runs as a cronjob every minute using the data from the database gathered by the registerVote.php script. The graphs are refreshed using javascript so the user does not have to refresh the page. I used the setInterval function for this as well. The function that refreshes the graphs looks something like:

function updateStats () {
  /* Send the date as a GET argument to make sure we get a new image every time */
  var date = escape(Date());

  YAHOO.util.Dom.setStyle('positive', 'background-image', 'url(graphs/positiveGraph.png?' + date + ')');
  YAHOO.util.Dom.setStyle('negative', 'background-image', 'url(graphs/negativeGraph.png?' + date + ')');
}

The date argument is sent along with the url so we won’t get a cached version of the image resulting in a never changing graph. The background image is positioned using CSS but we won’t need to change that since the graph shall be placed on the same position every time.

And that was about it I guess. If something is not clear, don’t hesitate to ask. And if you think something should have been done differently, please let me know. :)

Advertisements
This entry was posted in Work related. Bookmark the permalink.

3 Responses to Voting using YUI sliders and AJAX

  1. bhaarat says:

    How about some demo maite?

  2. jack says:

    YES!! this is a great article. I havnt seen one around that combines slider with post/GET. So please please put up a simple demo!!

  3. Jac says:

    This has a lot of potential to replace all those static ‘vote’ components. You appear to be highly competent with code – how about a plugin? We’d pay money for it wiillingly..
    Nice work.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s