Making a Giveaway Randomizer App w/ jQuery

Demo Download

The first Tutorialzine giveaway has finished! Now is the time to draw the winners. But, as this is a web development blog, we cannot just run an ORDER BY RAND() MySQL query in phpMyAdmin. Even more, three different sets of prizes must be awarded. This calls for a bit more stylish approach - a dedicated randomizer jQuery app, especially made for choosing winners in competitions and giveaways.

The app is divided in three steps - in step one you provide a list of the contestants' name and email, divided by a comma (each contestant on a separate line). In the second step, you provide a prize name and a number, signifying the number of copies that have been offered. In the last step, you get a randomly selected list of contestants and their prizes.

The HTML

As usual, we start with the HTML markup and the new doctype. After this, we continue with the stylesheets. The first stylesheet is generated by the awesome fontface generator at fontsquirrel. It will allow us to use the non web-safe LeagueGothic font in every browser. The font files themselves reside in the LeagueGothic folder in the demo.

randomizer.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Making a Giveaway Randomizer App w/ jQuery | Tutorialzine Demo</title>

<link rel="stylesheet" type="text/css" href="LeagueGothic/fontface.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />

</head>

<body>

<div class="step" id="step1">
    <div class="section">
        <h1>Step 1</h1>
        <h2>Paste a CSV Formatted List of All The Contestants</h2>

        <textarea></textarea>
        <a href="#" class="button">Next</a>
    </div>
</div>

<div class="step" id="step2">
    <div class="section">
        <h1>Step 2</h1>
        <h2>Paste a CSV Formatted List of All The Prizes</h2>
        <textarea></textarea>
        <a href="#" class="button back">Back</a>
        <a href="#" class="button finish">Finish!</a>
    </div>
</div>

<div class="step" id="step3">
    <div class="section">
        <h1>Step 3</h1>
        <h2>Congratulations to the Winners!</h2>
        <div class="results"></div>
        <a href="#" class="button again">Again</a>
    </div>
</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="md5.js"></script>
<script src="script.js"></script>
</body>
</html>

As you will see in a moment, we are applying a number of styles to the body element and use it as a regular block container. Inside it we have the three .step divs, which contain their own set of headings, textareas and buttons.

As you will see in the next section of the tutorial, we are making the body three times wider than the browser window, and each section div is exactly 33.333% of its width. This makes the sections as wide as the browser (this remains so even if you resize the window).

Lastly we include the jQuery library, our script.js file, and another one - md5.js. JavaScript does not provide means of calculating md5 hashes, so we are including a pure JavaScript implementation of the md5() PHP function, created by the php.js project. We are going to need this in the final step, where we are pulling the commenter's avatars from Gravatar, using their email addresses.

randomize_step_one.jpg

The CSS

The next step in building the app, is laying down the CSS. Only the more interesting styles are presented here. You can see the rest in styles.css in the download archive.

styles.css - Part 1

html{

   /**
    *   The background of the HTML element is
    *   visible as the top and bottom dark strips.
    */

    background-color:#424242;
}

body{
    font-size:13px;
    color:#fff;
    font-family:Arial, Helvetica, sans-serif;

   /**
    *   Using the body element as a
    *   container for the animation
    */

    left:0;
    position:fixed;
    top:5px;
    bottom:5px;
    width:300%;
}

.step{
    /* Each step takes one third of the width */

    float:left;
    height:100%;
    width:33.3333%;
    overflow:hidden;
    position:relative;
}

/* Step 1 */

#step1{ background:url('img/bg_1.jpg') no-repeat center center #6f7c18;}

#step1 textarea{
    -moz-box-shadow:-7px 7px 0 #637018;
    -webkit-box-shadow:-7px 7px 0 #637018;
    box-shadow:-7px 7px 0 #637018;
}

#step1 a.button{
    -moz-box-shadow:-4px 4px 0 #637018;
    -webkit-box-shadow:-4px 4px 0 #637018;
    box-shadow:-4px 4px 0 #637018;
}

#step1 a.button:active{

    /* The pressed state of the button */

    -moz-box-shadow:-2px 2px 0 #637018;
    -webkit-box-shadow:-2px 2px 0 #637018;
    box-shadow:-2px 2px 0 #637018;
}

#step1 h1{ background-color:#4a5310;}

What is happening here, is that we are using the body element as a regular container and apply a fixed positioning on it. This keeps the markup of the page to a minimum and demonstrates that the body is no different from the other elements on the page. We are making the body three times the width of the browser window. As all the sizes are given in percentages, everything is going to scale even if you resize the browser.

Notice that we've applied a background color to the html element. This is visible as two darker strips in the top and bottom of the app.

The three steps are floated to the left and are 33.333% of the width of the body element, which makes them perfectly fill the width of the screen. Each step has an individual set of rules applied (thanks to the IDs of the step classes), such as a background image, different colors for the box shadows and the h1 heading. Only the classes of the first step are given here, step2 and step3 follow the same idea.

styles.css - Part 2

/* Each step contains a section, centered in the page */

.section{
    height:550px;
    left:50%;
    margin:-275px 0 0 -328px;
    position:absolute;
    top:50%;
    width:655px;
}

h1{
    /* The step text */

    font-family:'LeagueGothicRegular',Arial,Helvetica,sans-serif;
    font-size:60px;
    position:absolute;
    right:488px;
    text-align:right;
    top:0;
    width:5000px;
    padding:20px 70px 20px 20px;
    font-weight:normal;
}

h2{
    /* The instruction text */
    position:absolute;
    right:0;
    top:50px;
    font-weight:normal;
    font-style:italic;
}

h2,a.button{
    font-family:'Myriad Pro', Corbel, Arial, Helvetica, sans-serif;
}

.section textarea,
.section .results{
    background-color:#fcfcfc;
    border:0;
    bottom:100px;
    color:#888888;
    font-family:Arial,Helvetica,sans-serif;
    font-size:12px;
    height:230px;
    padding:20px;
    position:absolute;
    width:615px;

    outline:none;
    resize:none;
    overflow:auto;
}

Inside each step div is an element with a .section class. It is centered horizontally and vertically on the page with CSS. All the headings, textboxes and buttons are positioned in relation with the section and are also perfectly centered.

The most interesting part is probably the h1 heading, which is always shown in the left part of the screen (no matter how large the window is) and stops precisely at 488px from the right border of the section. It also uses LeagueGothicRegular, the fontface embedded font, the definition of which you can find defined in LeagueGothic/fontface.css

app_steps.jpg

The jQuery

Now it is time to actually make this little app work. Fire your engines, it's jQuery time!

script.js - Part 1

$(document).ready(function(){

    /* An object with element selectors and margin values */

    var buttonMargins = {
        '#step1 a.button'   : '-100%',
        '#step2 a.finish'   : '-200%',
        '#step2 a.back'     : 0,
        '#step3 a.again'    : 0
    }

    var b = $('body');

    // Adding a click event listener to
    // every element in the object:

    $.each(buttonMargins,function(key,val){
        $(key).click(function(){
            b.animate({marginLeft:val});
            return false;
        });
    });

    // An additional click handler for the finish button:

    $('#step2 a.finish').click(function(){

        var resultsDiv = $('#step3 .results');

        // Catching the errors with a try / catch statement:

        try{

            resultsDiv.empty();

            var contestants = parseCSV($('#step1 textarea').val(),'contestants'),
                prizes      = parseCSV($('#step2 textarea').val(),'prizes'),
                allPrizes   = [];

            // The second element of the prizes CSV is
            // the number of copies of the prize

            $.each(prizes, function(){
                for(var i=0;i<this.col2;i++){

                    // The allPrizes array contains
                    // one entry for each prize.

                    allPrizes.push(this.col1);
                }
            });

            if(allPrizes.length > contestants.length){
                throw 'There are more prizes than contestants!';
            }

            // Randomizing both the contestants and the prizes:

            contestants = contestants.shuffle();
            allPrizes   = allPrizes.shuffle();

            // Using Gravatar
            var gravatarURL =   'http://www.gravatar.com/avatar/-REPLACE-?size=50&default='+
                                encodeURIComponent('http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=50');

            // Generating the markup:
            for(var i=0;i<allPrizes.length;i++){
                var result = $('<div>',{className:'result'});

                // Using a pure JavaScript md5 implementation to generate the hash
                // of the email so we can fetch the avatar from Gravatar:

                result.append($('<img>',{
                    src: gravatarURL.replace('-REPLACE-',md5(contestants[i].col2.toLowerCase()))
                }));

                result.append($('<p>',{
                    className   : 'info',
                    title       : contestants[i].col1 + ', ' +contestants[i].col2,
                    html        : contestants[i].col1 + '<i>'+allPrizes[i]+'</i>'
                }));

                resultsDiv.append(result);
            }

        }
        catch (e){
            // Dispaly the error message:
            resultsDiv.append($('<p>',{className:'error',html:e}));
        }
    });

The script is enclosed in the document.ready event listening function. The first thing the script does is to attach a set of events to the buttons. These create an animated movement of the body via a negative margin value, and show the different steps. To keep us from having to write the event listeners individually, the script loops through the buttonMargins object and attaches the listeners for us.

The finish button gets special treatment, as when it is clicked, the CSV formatted data in the textareas has to be parsed and randomly combined to produce the winners. Notice how we use the md5 function we included earlier, to calculate the email hash on line 74, which is used to fetch the contestants's avatar from Gravatar.

step3-announcing-the-winners.jpg

script.js - Part 2

  function parseCSV(str, name){

        // A simple function for parsing CSV formatted text

        var arr = str.split('\n');

        if(!arr.length){
            throw 'The '+name+' list cannot be empty!';
        }

        var tmp = [];
        var retArr = [];

        for(var i=0;i<arr.length;i++){

            if(!arr[i]) continue;

            tmp = arr[i].split(',');

            if(tmp.length!=2){
                throw 'The '+name+' list is malformed!';
            }

            retArr.push({
                col1 : $.trim(tmp[0]),
                col2 : $.trim(tmp[1])
            })
        }

        return retArr;
    }

    // A method for returning a randomized array:
    Array.prototype.shuffle = function(deep){
        var i = this.length, j, t;
        while(i) {
            j = Math.floor((i--) * Math.random());
            t = deep && typeof this[i].shuffle!=='undefined' ? this[i].shuffle() : this[i];
            this[i] = this[j];
            this[j] = t;
        }
        return this;
    };

});

In the second part of script.js, you can see the simple parseCSV() function. It is designed to parse the contents of the two textareas and return an array of objects. Each object contains a col1 and col2 properties, which correspond to the data in the textareas. This is later used to produce the name / prize combinations.

Also, notice the shuffle method, which is defined on the Array prototype. Defining it this way makes it available to any array in the script. We use it to shuffle the contestants and the prizes, so that everyone has a fair chance of winning.

With this our simple randomizer app is ready!

Using the app

For smaller competitions, you can manually populate the list of contestants and prizes, but for larger ones, this is out of the question (for example in Tzine's giveaway there are over 200 contestants!). This is where the following query comes handy:

SELECT DISTINCT comment_author,comment_author_email FROM wp_comments WHERE comment_post_ID = 1132 AND comment_approved = 1

It selects the name and email of all the people who left a comment on the giveaway (remember to replace the comment_post_ID value). After you run this query in phpMyAdmin, just click export and choose CSV (remember to leave the delimiter field empty). Then just copy and paste the created CSV list into the app.

Share your suggestions and thoughts about the app in the comment section below!

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Sufian Hassan

wow really nice script keep the good work up. :)

Jean Gardy

Wonderful! This is exactly what i was looking for! Just one question: How can I integrate this with a facebook page? Like having all our fans as contestants?

Thank you.

Just another great script! Keep up the good work!

Firas S.

Lovely! I Like it. Thanks for the hard work.
I can see that only admins can benefit from this app. I mean how can I show the results to the contestants as in Step 3? Or is it only a matter of copying the names and prizes from the app's result and then paste it to the public?

Martin Angelov

For Tzine's giveaway, I am thinking of posting a screenshot of the winners.

Excelente!

Codeforest

Really nice effect and useful app. And some clean jQuery code.

Nice as always, thanks for sharing this.

Wow! I am working on a giveaway project and you have brought this just in time. You are awesome!

Keep Posting Man, Liked A Lot & Thumbs Up For The Post!

Antisocial

Thx

Sam-Mauris

Did a fast look through and it seemed that the prize winner rotates 1, 2, 3 then back to 1 and it repeats.

Martin Angelov

Everything is randomized, and as there are only three contestants, there is a chance to end up with the same result (or with the pattern you've observed) in consecutive tries.

With a larger number of contestants, however, this would become highly unlikely.

rob busby

This is very slick! I like the cool effects and the app is a bonus. Great work and thanks for this valuable information.

Wow, this looks great! I was wondering, is there any way to change a persons odds of being picked? I ask this because I have an idea of a virtual ticket raffle where people can get more than one ticket.