Building an extensible jQuery countdown plugin from scratch

Filed in Code 30 comments

UPDATE!
I’ve made an official release of this plugin, click here and check it out

It has been some time since I’ve last posted, I’ve been far too busy with a project I’ve been working on the past several months, but it’s almost ready and I hope I’ll get back to my weekly posting.

While working at this project, at some point I needed a countdown script to announce certain features being released on specific dates, maybe I’ve been a bit lazy and tried finding an already built countdown plugin to work with, but not much luck finding exactly what I was looking for so I’ve built my own.

Inspired from the countdown plugin video from The New Boston, I’ve decided to make this tutorial so everyone can be able to create their very own extensible jQuery countdown plugin.

If you don’t want to read the tutorial and just download the ready-to-use jQuery countdown script, then click here and read the instructions.txt file.

The point to build our own countdown script is to be able to have full control over it, to make it extensible and to be able to reuse it throughout our projects very easily.

Setting up our workspace

For this, we will obviously need the jQuery library which you can download from their website right here, but if you already have it, do make sure it’s the last stable version.

I’m assuming you already know how to create files and folders :) so create a folder for the CSS and one for the JavaScript files, because we want to be clean and work with unobtrusive code.

You can also download my clean workspace for this tutorial clicking here.

It’s entirely your decision where the countdown is positioned and how it is styled, but to keep this tutorial simple, we’ll use a basic look & feel and afterwards you can implement it as you wish.

Let’s create the HTML structure for the countdown:

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>jQuery countdown extensible plugin</title>
    <link rel="stylesheet" type="text/css" href="css/countdown.css">
  </head>
  <body>
    <ul id="countdown">
      <li>
        <span class="days">00</span>
        <p class="timeRef">days</p>
      </li>
      <li>
        <span class="hours">00</span>
        <p class="timeRef">hours</p>
      </li>
      <li>
        <span class="minutes">00</span>
        <p class="timeRef">minutes</p>
      </li>
      <li>
        <span class="seconds">00</span>
        <p class="timeRef">seconds</p>
      </li>
    </ul>
  </body>
</html>

The external stylesheet which goes in the CSS folder, containing some basic styles:

countdown.css

ul#countdown {
  margin: 10px auto;
  padding: 0;
}
ul#countdown li{
  background-color: #333;
  border: 2px solid #fff;
  color: #fff;
  display: inline;
  float: left;
  font: normal bold 36px Arial;
  letter-spacing: 5px;
  margin-left: 5px;
  text-align: center;
  width: 60px;
  border-radius: 6px;
  box-shadow: 0 0 3px #777;
}
ul#countdown span {
  padding-left: 5px;
}
ul#countdown  p.timeRef {
  font: normal normal 12px Arial;
  letter-spacing: 0;
  margin: 0;
  padding-bottom: 5px;
}

And in the JS folder goes the jQuery library.

Right now all we have is a static looking countdown, so let’s add the logic and build the plugin.
In the JS folder, create a file countdown.js, open it with your favorite code editor and let’s get to work.

We will build all of our code inside a plugin function that jQuery recognizes:

countdown.js

(function($) {
  $.fn.countdown = function() {
 
    // plugin code here
     
  }
}) (jQuery);

A jQuery plugin function requires a name and in our example we gave it countdown
Also, you’ll notice that the main function has a dollar sign parameter and the jQuery keyword at the end between paranthesis, if we wouldn’t have done that, we no longer could’ve used the short jQuery dollar sign namespace (i.e. $(“h1″) ) and had to use jQuery’s regular namespace (i.e. jQuery(“h1″) ).

It’s always good practice to test your functions before you start coding, a quick way to do it is to add a simple alert function and then just call it from an HTML page:

countdown.js

(function($) {
  $.fn.countdown = function() {
 
    alert("Hello!");
     
  }
}) (jQuery);

Now let’s test the plugin by calling it from our index.html

index.html

...

<head>
  <title>jQuery countdown extensible plugin</title>
  <script type="text/javascript" src="js/jquery-1.6.4.js"></script>
  <script type="text/javascript" src="js/countdown.js"></script>
  <link rel="stylesheet" type="text/css" href="css/countdown.css">
  <script type="text/javascript">
 
    $.fn.countdown();
 
  </script>
</head>

....

If you didn’t mistype anything, you should get an alert with the message “Hello!” when you open or refresh the page.

When you get different results than those mentioned in this article, try downloading the examples.

The beauty of a plugin is the control we can add to it by extending it with parameters where we can specify certain settings and a callback function that launches a code we want after it has successfully finished processing.
We’ll add two parameters to our plugin function, settings and callback.

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {
 
    //code here
     
  }
}) (jQuery);

Now that we have specified that our plugin requires two parameters, options and callback, let’s start adding our custom settings.
In our case, we’ll need to be able to add a date which we can make the plugin read and process.
We’ll specify our settings via an array, but we’ll start with just one setting for now:

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {
 
    //custom 'this' selector
    thisEl = $(this);
 
    // array of custom settings
    var settings = {
      'date': null,
    };
     
  }
}) (jQuery);

By default, we’ve set it to null so it would be empty if no date will be specified when calling the plugin.
Also, you’ll notice at the begining a new variable called thisEl (which can be called whatever you want), we added the $(this) selector onto this variable so we can easily reference our countdown’s html code later on. If you do not know what this means in JavaScript, do look it up as it’s one of the key elements of JavaScript.

To make the plugin’s options parameter recognize our settings array and reference all of our settings from it, we’ll use jQuery’s $.extend function

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {
 
    //custom 'this' selector
    thisEl = $(this);
 
    // array of custom settings
    var settings = {
      'date': null,
    };
   
    // append the settings array to options
    if(options) {
      $.extend(settings, options);
    }
  }
}) (jQuery);

We’ve told it that if the options parameter exists, then append the settings array to it.

So now we’re able to call the plugin and specify a date.

index.html

<!DOCTYPE html>
<html>
<head>
  <title>jQuery countdown plugin - devingredients.com</title>
  <link rel="stylesheet" type="text/css" href="css/countdown.css">
  <script type="text/javascript" src="js/jquery-1.6.4.js"></script>
  <script type="text/javascript" src="js/countdown.js"></script>
 
  <script type="text/javascript">
    $(document).ready(function() {
   
      $("#countdown").countdown({
        date: "4 december 2011 11:20:00", //add the countdown's end date (i.e. 3 november 2012 12:00:00)
      });
     
    });
  </script>
 
</head>
<body>
  <ul id="countdown">
    <li>
      <span class="days">00</span>
      <p class="timeRef">days</p>
    </li>
    <li>
      <span class="hours">00</span>
      <p class="timeRef">hours</p>
    </li>
    <li>
      <span class="minutes">00</span>
      <p class="timeRef">minutes</p>
    </li>
    <li>
      <span class="seconds">00</span>
      <p class="timeRef">seconds</p>
    </li>
  </ul>
</body>
</html>

We’ve applied the plugin onto an HTML’s countdown ID that holds the HTML elements for the countdown, this way the plugin will be much faster and won’t have to tranverse through the entire DOM to find the elements that it needs to interact with.
Now that we’re done with the basics, let’s get to the real processing.

But first! Let’s see how we can read the date value we specify when calling the plugin.
We’re going to create a new function inside our plugin called countdown_proc and we’re going to alert the date.

countdown.js

...

//create the countdown processing function
function countdown_proc() {
 
  alert(settings['date'];
 
}

//run the function
countdown_proc();

...

Since we assigned the settings array to be the place where we specify all of our properties, to fetch the date we use settings['date']. And if you add more properties, just use settings['PROPERTY_NAME'].
Now when you open or refresh the page, it will alert the date value you specify in your html page when calling the plugin.

Remove the alert and let’s make this plugin actually do something ;)
We’ll need two variables that will hold the current and end date. We’ll get the current date using the jQuery’s $.now() function that fetches the current date. The end date will be specified when calling the plugin so we’ll fetch it from there.

countdown.js

function countdown_proc() {
 
  var eventDate = Date.parse(settings['date']) / 1000;
  var currentDate = Math.floor($.now() / 1000);
 
}

To avoid confusion, let’s talk about date for a bit. We created the variable eventDate and we need to asign the date we specify when calling the plugin, like mentioned in the previous example (i.e. date: “4 december 2011 11:20:00″ ).
But what we really need is a timestamp date (number of seconds). So when we fetch the date value, we’ll also convert it into a timestamp date by using the Date.parse function, this will however give us the timestamp in milliseconds so we just divide it by 1000. Thus Date.parse(settings['date']) / 1000 results the timestamp in seconds.
As for the currentDate, jQuery has a function named $.now() that fetches the current date in milliseconds, again, we divide it by 1000 to work with seconds.

We have both variables for Current Date and for End Date, we’ll need to calculate the difference so we can populate the countdown with the difference between them, the remaining time that is.
We’ll create a variable seconds where we’ll do a simple math substraction to find out the number of seconds between the current and end date.

countdown.js

function countdown_proc() {
 
  var eventDate = Date.parse(settings['date']) / 1000;
  var currentDate = Math.floor($.now() / 1000);
 
  var seconds = eventDate - currentDate;
 
}

Now that we know the remaining time in seconds, let’s calculate the number of days, hours, minutes and seconds and add them to variables which we can later use.

countdown.js

function countdown_proc() {
 
  var eventDate = Date.parse(settings['date']) / 1000;
  var currentDate = Math.floor($.now() / 1000);
 
  var seconds = eventDate - currentDate;
 
        var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
  seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
 
  var hours = Math.floor(seconds / (60 * 60));
  seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
 
  var minutes = Math.floor(seconds / 60);
  seconds -= minutes * 60; //update the seconds variable with no. of minutes removed

}

Let’s explain days and you’ll figure out the rest of the math. To calculate the number of days, we used a simple math calculation where we divided the total number of seconds by a day (in seconds). A day has 86400 seconds, but just so we don’t have to remember how many seconds a period of time has, we just used (hours * minutes * seconds), equaling the number of seconds in a day. Thus, number of seconds divided by (60 * 60 * 24) => seconds / seconds in a day. Wow, I think I’m being way too explanatory here.
The Math.floor function that surrounds our math calculation does nothing more but to remove the remainder from our result and consider only the floor value of it, just so we won’t get results such as 3.3164400 days.

You’ll notice the seconds variable right afterwards, doing a math calculation with -=. What that does is remove the number of seconds used in the days variable so we can have an updated seconds variable without the number of days in order to continue calculating the hours, minutes and seconds.

var1 -= 3;

is the same as

var1 = var1 - 3;

And the process repeats for hours, minutes.

Finally the fun part, let’s populate our countdown’s html span values:

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {
 
    //custom jQuery 'this' selector
    thisEl = $(this);
 
    // array of custom settings
    var settings = {
      'date': null,
    };
   
    // append the settings array to options
    if(options) {
      $.extend(settings, options);
    }
   
    function countdown_proc() {
     
      var eventDate = Date.parse(settings['date']) / 1000;
      var currentDate = Math.floor($.now() / 1000);
     
      var seconds = eventDate - currentDate;
     
      var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
      seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
     
      var hours = Math.floor(seconds / (60 * 60));
      seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
     
      var minutes = Math.floor(seconds / 60);
      seconds -= minutes * 60; //update the seconds variable with no. of minutes removed
     
      //update the countdown's html values.
      thisEl.find(".days").text(days);
      thisEl.find(".hours").text(hours);
      thisEl.find(".minutes").text(minutes);
      thisEl.find(".seconds").text(seconds);
     
    }
   
    //run the countdown_proc function
    countdown_proc();

  }
}) (jQuery);

If everything went well, each time you refresh the page and you should see the countdown updated with the remaining time.
We’ve used the thisEl variable to start working directly with the code of our countdown html portion, using .find to find the elements we want to update.

We do need to make the countdown update itself each second though, so we’ll just loop the process every second with the help of JavaScript’s setInterval function, we’ll add it after the countdown_proc function:

countdown.js

//loop the function
interval = setInterval(countdown_proc, 1000);

Now you should see the countdown updating itself each second.

setInterval requires the code you want to run and at what interval you want it to run (in milliseconds).
We’ve run it by also adding it to an interval variable so we can have control over it and in our case, stop it whenever the countdown finishes.

Even though the countdown’s working now, we still have a few more things to fix, or at least to consider.

If you’ll call the plugin with an event date that happens in a minute or so, you’ll notice that once it reaches time 0, it will not stop and start showing negative values. Let’s fix that.

Right after the eventDate and currentDate variables, let’s add the following condition

countdown.js

...

  if(eventDate <= currentDate) {
    callback.call(this);
    clearInterval(interval);
  }
     
...

What the condition basically does, is check whether the eventDate variable (that holds the timestamp date value of our event) is equal or less than the currentDate variable, if so, run the callback (the one we’ll specify when we’ll call the script in out html page) and also to clear the interval, at that point we no longer need the interval to keep looping our countdown script since the countdown ended.

Hoping that all of that was clear, let’s test it one more time and also make it alert a message when the countdown ends.

index.html

<!DOCTYPE html>
<html>
<head>
  <title>jQuery countdown plugin - devingredients.com</title>
  <link rel="stylesheet" type="text/css" href="css/countdown.css">
  <script type="text/javascript" src="js/jquery-1.6.4.js"></script>
  <script type="text/javascript" src="js/countdown.js"></script>
 
  <script type="text/javascript">
    $(document).ready(function() {
   
      $("#countdown").countdown({
        date: "18 february 2012 17:07:00", // add the countdown's end date (i.e. 3 november 2012 12:00:00)
      }, function() {
        alert("Countdown finished!");
      });
     
    });
  </script>
 
</head>
<body>
  <ul id="countdown">
    <li>
      <span class="days">00</span>
      <p class="timeRef">days</p>
    </li>
    <li>
      <span class="hours">00</span>
      <p class="timeRef">hours</p>
    </li>
    <li>
      <span class="minutes">00</span>
      <p class="timeRef">minutes</p>
    </li>
    <li>
      <span class="seconds">00</span>
      <p class="timeRef">seconds</p>
    </li>
  </ul>
</body>
</html>

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {

    //custom 'this' selector
    var thisEl = $(this);

    // array of custom settings
    var settings = {
      'date': null,
    };
   
    // append the settings array to options
    if(options) {
      $.extend(settings, options);
    }
      function countdown_proc() {
       
        var eventDate = Date.parse(settings['date']) / 1000;
        var currentDate = Math.floor($.now() / 1000);
       
        if(eventDate <= currentDate) {
          callback.call(this);
          clearInterval(interval);
        }
       
        var seconds = eventDate - currentDate;
       
        var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
        seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
       
        var hours = Math.floor(seconds / (60 * 60));
        seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
       
        var minutes = Math.floor(seconds / 60);
        seconds -= minutes * 60; //update the seconds variable with no. of minutes removed
       
        //update the countdown's html values.
        thisEl.find(".days").text(days);
        thisEl.find(".hours").text(hours);
        thisEl.find(".minutes").text(minutes);
        thisEl.find(".seconds").text(seconds);
       
    }
    countdown_proc();
   
    //loop the function
    interval = setInterval(countdown_proc, 1000);
   
  }
}) (jQuery);

As callback, we added an alert with the message “Countdown finished” that will show up when the countdown ends.
Now you have a functional countdown which you can use to announce features on a website, or perhaps the launch of an website itself.

We can however enhance this countdown even further.

Constant double digits setting

Depending on how you style the countdown, it can become inconsistent if the time displays both in single and double digits (i.e. 5:0:54 / 17:15:54), or maybe you want it to aways display with double digits for visual reasons only.
To achieve that, we’ll extend this plugin by adding one more setting, that is to turn on and off the display of double digits.
We’ll start by adding format as our new setting name with the value of null, in our settings array:

countdown.js

...

var settings = {
  'date': null,
  'format': null
};

...

We’ve set the value to null because we want it to hold no value as long as we do not set it when we call the plugin.
In our countdown.js script file, right after the last seconds variable we will add the following condition:

countdown.js

....

if(settings['format'] == "on") {
  days = (String(days).length >= 2) ? days : "0" + days;
  hours = (String(hours).length >= 2) ? hours : "0" + hours;
  minutes = (String(minutes).length >= 2) ? minutes : "0" + minutes;
  seconds = (String(seconds).length >= 2) ? seconds : "0" + seconds;
}

...

The reason why we want to have this condition set after the days, hours, minutes, seconds variables is because we want those variables to already exist and hold the real countdown values so we can now check whether their values are formed by two digits or not, and if not, we’ll add the missing 0.
So what we’ve done is to check if the format settings is set to ON and if so, check whether the values are formed by two digits and if not, add the missing zero.
The condition is quite simple and it’s exacty as an if-and-else statement.

Basically:

days = (String(days).length >= 2) ? days : "0" + days;

Is the same as:

if (String(days).length >= 2) {
  days = days;
} else {
  days  = "0" + days;
}

So if the days variable has two digits or more, leave it as it is, otherwise it must be made of just 1 character so append a 0 to it.

As a result we should have:

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {

    //custom 'this' selector
    var thisEl = $(this);

    // array of custom settings
    var settings = {
      'date': null,
      'format' : null
    };
   
    // append the settings array to options
    if(options) {
      $.extend(settings, options);
    }
      function countdown_proc() {
       
        var eventDate = Date.parse(settings['date']) / 1000;
        var currentDate = Math.floor($.now() / 1000);
       
        if(eventDate <= currentDate) {
          callback.call(this);
          clearInterval(interval);
        }
       
        var seconds = eventDate - currentDate;
       
        var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
        seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
       
        var hours = Math.floor(seconds / (60 * 60));
        seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
       
        var minutes = Math.floor(seconds / 60);
        seconds -= minutes * 60; //update the seconds variable with no. of minutes removed
       
        if(settings['format'] == "on") {
          days = (String(days).length >= 2) ? days : "0" + days;
          hours = (String(hours).length >= 2) ? hours : "0" + hours;
          minutes = (String(minutes).length >= 2) ? minutes : "0" + minutes;
          seconds = (String(seconds).length >= 2) ? seconds : "0" + seconds;
        }
       
        //update the countdown's html values.
        thisEl.find(".days").text(days);
        thisEl.find(".hours").text(hours);
        thisEl.find(".minutes").text(minutes);
        thisEl.find(".seconds").text(seconds);
       
    }
    countdown_proc();
   
    //loop the function
    interval = setInterval(countdown_proc, 1000);
   
  }
}) (jQuery);

and to force it to display two digits, we’ll call the plugin by specifying our format setting to ON.

index.html

<script type="text/javascript">
  $(document).ready(function() {
 
    $("#countdown").countdown({
      date: "18 november 2011 17:07:00", // add the countdown's end date (i.e. 3 november 2012 12:00:00)
      format: "on" // on (03:07:52) | off (3:7:52) - two_digits set to ON maintains layout consistency
    }, function() {
      alert("Countdown finished!");
    });
   
  });
</script>

Now the plugin has two settings to play with.
But it’s not yet an entirely fail-safe plugin, I say we should add a warning in case the date that’s added when calling the plugin is incorrect.
To do so all we have to do is wrap our *.text() methods in a condition where we check whether our eventDate variable is a number, if it’s not, that means the specified date is invalid and has been no longer successfully converted into a numerical timestamp.

countdown.js

//update the countdown's html values.
if(!isNaN(eventDate)) {
  thisEl.find(".days").text(days);
  thisEl.find(".hours").text(hours);
  thisEl.find(".minutes").text(minutes);
  thisEl.find(".seconds").text(seconds);
} else {
  alert("Invalid date. Here's an example: 12 Tuesday 2012 17:30:00");
  clearInterval(interval);
}

We used the isNaN function (which means is Not a Number) and prepended an exclamation mark to it to reverse the condition, so now it’s checking whether it is a number (!isNaN = if it’s not Not a Number).
And if it’s not, we just apply an alert with a warning and clear the interval to stop the timer.

What about the S endings of day(s) minute(s) when it actually display 1 as value?
We should make it display day when it shows 1 day and days when there are more days left.

Conditioning the Ss

To start, we’ll need to add a different class for each time reference (timeRef for days will become timeRefDays, for hours it will be timeRefHours and so on).

index.html

<!DOCTYPE html>
<html>
<head>
  <title>jQuery countdown plugin - devingredients.com</title>
  <link rel="stylesheet" type="text/css" href="css/countdown.css">
  <script type="text/javascript" src="js/jquery-1.6.4.js"></script>
  <script type="text/javascript" src="js/countdown.js"></script>
 
  <script type="text/javascript">
    $(document).ready(function() {
   
      $("#countdown").countdown({
        date: "4 december 2011 11:10:50", // add the countdown's end date (i.e. 3 november 2012 12:00:00)
        format: "on" // on (03:07:52) | off (3:7:52) - two_digits set to ON maintains layout consistency
      },
     
      function() {
       
        // the code here will run when the countdown ends
        alert("done!")

      });
    });
  </script>
 
</head>
<body>
  <ul id="countdown">
    <li>
      <span class="days">00</span>
      <p class="timeRefDays">days</p>
    </li>
    <li>
      <span class="hours">00</span>
      <p class="timeRefHours">hours</p>
    </li>
    <li>
      <span class="minutes">00</span>
      <p class="timeRefMinutes">minutes</p>
    </li>
    <li>
      <span class="seconds">00</span>
      <p class="timeRefSeconds">seconds</p>
    </li>
  </ul>
</body>
</html>

countdown.css

ul#countdown {
  margin: 10px auto;
  padding: 0;
}
ul#countdown li{
  background-color: #333;
  border: 2px solid #fff;
  color: #fff;
  display: inline;
  float: left;
  font: normal bold 36px Arial;
  letter-spacing: 5px;
  margin-left: 5px;
  text-align: center;
  width: 60px;
  border-radius: 6px;
  box-shadow: 0 0 3px #777;
}
ul#countdown span {
  padding-left: 5px;
}
ul#countdown  p.timeRefDays, ul#countdown  p.timeRefHours, ul#countdown  p.timeRefMinutes, ul#countdown  p.timeRefSeconds{
  font: normal normal 12px Arial;
  letter-spacing: 0;
  margin: 0;
  padding-bottom: 5px;
}

We already have access to our variables that display the coundown time, all we have to check is whether it’s 1 or not. If any time duration is 1, remove the s, otherwise show it.
Right before our format setting code, let’s add conditions for each time reference:

countdown.js

...

//conditional Ss
if (days == 1) { thisEl.find(".timeRefDays").text("day"); } else { thisEl.find(".timeRefDays").text("days"); }
if (hours == 1) { thisEl.find(".timeRefHours").text("hour"); } else { thisEl.find(".timeRefHours").text("hours"); }
if (minutes == 1) { thisEl.find(".timeRefMinutes").text("minute"); } else { thisEl.find(".timeRefMinutes").text("minutes"); }
if (seconds == 1) { thisEl.find(".timeRefSeconds").text("second"); } else { thisEl.find(".timeRefSeconds").text("seconds"); }

...

And there we have it! Man, me and my long articles :/
I hope that all the details at each step somehow helped you and didn’t actually clutter your mind more hehe

Here’s the final code:

index.html

<!DOCTYPE html>
<html>
<head>
  <title>jQuery countdown plugin - devingredients.com</title>
  <link rel="stylesheet" type="text/css" href="css/countdown.css">
  <script type="text/javascript" src="js/jquery-1.6.4.js"></script>
  <script type="text/javascript" src="js/countdown.js"></script>
 
  <script type="text/javascript">
    $(document).ready(function() {
   
      $("#countdown").countdown({
        date: "23 february 2012 1:50:30", // add the countdown's end date (i.e. 3 november 2012 12:00:00)
        format: "on" // on (03:07:52) | off (3:7:52) - two_digits set to ON maintains layout consistency
      },
     
      function() {
       
        // the code here will run when the countdown ends
        alert("done!")

      });
    });
  </script>
 
</head>
<body>
  <ul id="countdown">
    <li>
      <span class="days">00</span>
      <p class="timeRefDays">days</p>
    </li>
    <li>
      <span class="hours">00</span>
      <p class="timeRefHours">hours</p>
    </li>
    <li>
      <span class="minutes">00</span>
      <p class="timeRefMinutes">minutes</p>
    </li>
    <li>
      <span class="seconds">00</span>
      <p class="timeRefSeconds">seconds</p>
    </li>
  </ul>
</body>
</html>

countdown.css

ul#countdown {
  margin: 10px auto;
  padding: 0;
}
ul#countdown li{
  background-color: #333;
  border: 2px solid #fff;
  color: #fff;
  display: inline;
  float: left;
  font: normal bold 36px Arial;
  letter-spacing: 5px;
  margin-left: 5px;
  text-align: center;
  width: 60px;
  border-radius: 6px;
  box-shadow: 0 0 3px #777;
}
ul#countdown span {
  padding-left: 5px;
}
ul#countdown  p.timeRefDays, ul#countdown  p.timeRefHours, ul#countdown  p.timeRefMinutes, ul#countdown  p.timeRefSeconds{
  font: normal normal 12px Arial;
  letter-spacing: 0;
  margin: 0;
  padding-bottom: 5px;
}

countdown.js

(function($) {
  $.fn.countdown = function(options, callback) {

    //custom 'this' selector
    var thisEl = $(this);

    //array of custom settings
    var settings = {
      'date': null,
      'format': null
    };

    //append the settings array to options
    if(options) {
      $.extend(settings, options);
    }
   
    //main countdown function
    function countdown_proc() {
     
      var eventDate = Date.parse(settings['date']) / 1000;
      var currentDate = Math.floor($.now() / 1000);
     
      if(eventDate <= currentDate) {
        callback.call(this);
        clearInterval(interval);
      }
     
      var seconds = eventDate - currentDate;
     
      var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
      seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
     
      var hours = Math.floor(seconds / (60 * 60));
      seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
     
      var minutes = Math.floor(seconds / 60);
      seconds -= minutes * 60; //update the seconds variable with no. of minutes removed
     
      //conditional Ss
      if (days == 1) { thisEl.find(".timeRefDays").text("day"); } else { thisEl.find(".timeRefDays").text("days"); }
      if (hours == 1) { thisEl.find(".timeRefHours").text("hour"); } else { thisEl.find(".timeRefHours").text("hours"); }
      if (minutes == 1) { thisEl.find(".timeRefMinutes").text("minute"); } else { thisEl.find(".timeRefMinutes").text("minutes"); }
      if (seconds == 1) { thisEl.find(".timeRefSeconds").text("second"); } else { thisEl.find(".timeRefSeconds").text("seconds"); }
     
      //logic for the two_digits ON setting
      if(settings['format'] == "on") {
        days = (String(days).length >= 2) ? days : "0" + days;
        hours = (String(hours).length >= 2) ? hours : "0" + hours;
        minutes = (String(minutes).length >= 2) ? minutes : "0" + minutes;
        seconds = (String(seconds).length >= 2) ? seconds : "0" + seconds;
      }
     
      //update the countdown's html values.
      if(!isNaN(eventDate)) {
        thisEl.find(".days").text(days);
        thisEl.find(".hours").text(hours);
        thisEl.find(".minutes").text(minutes);
        thisEl.find(".seconds").text(seconds);
      } else {
        alert("Invalid date. Here's an example: 12 Tuesday 2012 17:30:00");
        clearInterval(interval);
      }
    }
   
    //run the function
    countdown_proc();
   
    //loop the function
    interval = setInterval(countdown_proc, 1000);
   
  }
}) (jQuery);

Once you’ve read the entire tutorial, all the code should no longer look difficult at all, in fact, keep spending some time playing with it, get comfortable with it, add more control as needed.

In fact, I’ll reward the first person who makes the countdown change its color once it’s about to end, and the time that it should start changing its color should be specified as a setting.

Reward: A free link in the footer of this blog for 3 whole months* :)

* Yes, that little pesky star, I’ll have to specify that I won’t be accepting any website that contains rude, obscene or racist content.

The code is free to use for both personal and commercial purposes, however, do try to subscribe, tweet, like and so on… share the love!

Good luck!

UPDATE!
I’ve made an official release of this plugin, click here and check it out

Posted by Catalin   @   22 November 2011 30 comments
Tags : , ,

Share This Post

RSS Digg Twitter StumbleUpon Delicious Technorati

30 Comments

Comments
Nov 23, 2011
2:54 AM
#1 Bud :

Awesome Tutorial Catalin-I have never used jquery but to link to it as part of a script.

I am going to try and ‘setting’ to change color as you challenged.

Author Nov 23, 2011
10:18 AM
#2 Catalin :

Good to hear from you, Bud :-)

If you manage to add the setting, then my friend, you can add jQuery to your web development skills.

Good luck!

Jan 2, 2012
8:51 AM

I’ve never used this jQuery. This time ‘m going to try out, well thanks for the tutorial, its been a great help.

Author Jan 2, 2012
8:54 PM
#4 Catalin :

@michelleweb
I’m glad it has helped you. jQuery is now a main dev ingredient :-) I strongly recommend that you dive in its world as well.

Feb 13, 2012
1:00 PM
#5 Doane*16 :

Thanks for the well indeed tutorial in creating the HTML structure for the countdown, But I can’t say that I’m good enough to catch up immediately the ideas that you share here, Because I’m not really familiar about it. However, Thanks a lot.

Mar 10, 2012
9:12 PM
#6 Andrew :

Where is this countdown plg going to? :) ) Ok, I must have the countdown with negative values after 0 time. I delete the code from “if(eventDate <= currentDate) {". And what I can find here? First – the first negative sign. It's right, couse we are on the second side of the 0. Second – is the time upgoing? NO, the time is downgoing to … the midnight of today!!! (start date 01 march 2012 00:00:00) Can you fixt it?
Second matter – how much of instances of this script can be displayed at one page? I need from 10 to 50, with different times. brgs, thank you for your lesson of Query, very fine for beginners. Thank You.

Mar 11, 2012
12:26 AM
#7 Andrew :

Math.abs(eventDate

Mar 11, 2012
1:12 AM
#8 Andrew :

can you change the incoming date format into (yyyy-mm-dd hours:minutes:seconds) or (yyy,mm,dd,hours,minutes,seconds)? It will be better to load it from dbase ..

Author Mar 11, 2012
8:20 PM
#9 Catalin :

I’m having a hard time understanding the end result you’re expecting for this countdown to do.

Can you please describe in details exactly what you’re trying to achieve? I’ll see if I’ll find the time to do it for you :-)

Aug 9, 2012
4:10 AM
#10 Unity3D :

Is reward gone..? :D

Thanks for this one, mate, it’s great in so many aspects ! Keep on posting, that will be greatest reward. :)

Cheers

Aug 9, 2012
4:37 AM
#11 Unity3D :

And, I forgot a small ”update” – in my native language, every number that ends with 1 is singular word (20 days, but 21 day), so in this case “Ss” must detect last number. I achieve that with assigning new var to check substr(id.length – 1) (where id is days/hours/minutes/secs), and then put that new var in if statement –

if (newVar == 1) { selector.find(‘timeRefDays’…)

Just for the record, if anyone else needs this. :)

Author Aug 9, 2012
9:40 AM
#12 Catalin :

@Unity3D
Reward is still available :)
Awesome work! I didn’t know about your special case of numbers ending in 1 being as singular words.
Thanks for contributing with the solution!

Aug 22, 2012
6:29 AM
#13 Ryan :

First of all, this was an awesome tutorial. I love that you went into this much detail because it allowed me to actually understand a few new aspects of jQuery. I much prefer an approach like this than simply a block of code to be copied in order to perform some function.

That said, after building this plugin and adapting it to count down from an arbitrary amount of time (10 mins), I had to abandon it and extract the functionality to a regular set of functions for one simple reason: Even though I was able to get the countdown to work, I could not figure out how to stop the countdown through a click event on a button, which was an essential part of what I was trying to accomplish.

If you could add some instruction and explanation on how to stop/clear the countdown by clicking on some element (button, div, etc.) – and how to more generally call functions inside of a plugin directly – then I think that would make the plugin complete and even more extensible.

Author Aug 22, 2012
9:48 AM
#14 Catalin :

@Ryan
Thanks for the awesome feedback!
That sure sounds like an essential feature this plugin should have, I’ll do it somewhere today, update the plugin and get back to you via mail :)

P.S. I’m already recoding this plugin entirely and planning to release soon a better built and customizable plugin with essential features, such as real-time (timezone based) countdown, custom duration length and more.

I’ll also consider all the feature requests from these comments.

Cheers

Author Aug 22, 2012
10:32 PM
#15 Catalin :

@Ryan
In the end, I had to tweak the plugin and make a custom version and I mailed it to you, I’m not publishing these changes as they’re going to be featured in the new version I’m planning to release soon.
Let us know how things work out for you :)
Cheers

Aug 23, 2012
12:22 AM
#16 Ryan :

Hey Catalin,

Thanks. I’ll check it out and get back to you.

Aug 24, 2012
2:35 AM
#17 Ryan :

Hi Catalin,

I just now had a chance to take a look at the plugin. Thanks again for that. Everything looks good. My only question is: Do the pause, stop and start functions need to be declared inside of the countdown_proc() function or could they be declared outside of it?

Actually, one more question: Do you need both of these?

//run the function
countdown_proc();

//loop the function
interval = setInterval(countdown_proc, 1000);

Wouldn’t the loop start the function anyway?

Thanks,
Ryan

Author Aug 24, 2012
10:49 AM
#18 Catalin :

@Ryan
You can move them outside of the function, yes. Mind me asking why you’d want to do that?

And yes, you need both calls to that function. If you’d leave only the setInterval, the countdown would have one second delay before it would start and I doubt you want that. The fact that we call it before regularly:

//run the function
countdown_proc();

starts instantly and then the setInterval continues by looping it ‘after’ each second:

//loop the function
interval = setInterval(countdown_proc, 1000);

Aug 25, 2012
7:08 AM
#19 Ryan :

Hi Catalin,

It’s not so much that I would necessarily make that changed for this particular plugin. I’m just trying to get a better understanding of how to write and work with jQuery plugins. I think it would be a great idea for a tutorial to cover that topic in the same detail as you’ve covered this tutorial for creating a specific countdown plugin. So far I’ve found it somewhat difficult to find resources that explain all the plugin concepts clearly.

As for starting the procedure and then starting the loop and the fact that just starting the loop would cause a one second delay … in this particular instance that does actually accomplish what I was looking for. On the project I’m on, there’s a 10 minute counter that start when you press a button and that button also happens to start a process with an AJAX call that will stop at the end of 10 minutes. But starting with the loop instead of starting the process directly, I actually see the one second display of 10:00 rather than visually starting with 9:59. I actually want to see that 10:00 for a second and it gives the process a second to start through the AJAX call.

Ryan

Author Aug 25, 2012
12:47 PM
#20 Catalin :

I’m glad you managed to tweak it so it would fit your needs, then.
A tutorial about extending a plugin with methods sounds good, but it will have to be after I finish the countdown, as I’m working on an official release.

Cheers

Sep 9, 2012
3:50 AM
#21 R.D. Miller :

Hi Catalin, the countdown doesn’t seem to work in IE9 Here is the example loaded: http://rewards.shophqf.com/js/020-final/index.html

In exchange for your help (or if anyone else would like). I have modified this script to allow for multiple countdowns on one page and the ability to also start the timer with a timestamp from the web server. I would be happy to share it with anyone that is interested.

Sep 9, 2012
4:08 AM
#22 R.D. Miller :

I found the problem! This is really weird, but for some reason, IE9 does not like the tag. If you replace all the with it counts down fine in IE9 too!

Sep 9, 2012
4:09 AM
#23 R.D. Miller :

sorry that’s the span tag should be replaced with the p tag.

Sep 24, 2012
9:04 PM
#24 krish :

Hey how can i change the language, when i change this its coming in Eng only, can you suggest me how to change the days ,hours…etc to other languages

Author Sep 24, 2012
9:16 PM
#25 Catalin :

@krish If you can wait just a few days, I’m going to release an updated version with more control, one of which is the language.

Nov 21, 2012
12:29 PM
#26 Pete :

Thank you for the useful tutorial – I’m working on making a simple countdown Android app to learn jQuery – this will definitely help!

Author Nov 21, 2012
12:31 PM
#27 Catalin :

@Pete
Glad it helped you! You can also checkout my release of this plugin: http://devingredients.com/jcounter/

Trackbacks to this post.
Leave a Comment

Previous Post
«
Next Post
»
Luix designed by Video Game Music In conjunction with VPS Hosting , Website Hosting and Shared Hosting.