Ajax/Javascript php countdown timer

Hey all,

I’m trying to build a countdown timer for an auction website.
While I can probably get a simple js timer based on setinterval() I was wondering if there’s a way to sync it with the server at a specific interval instead of just having it run on the client side for the entire duration.
From my understanding a simple js timer will eventually “drift” off so to speak and not be in sync with the server time after a while.
Any way to calculate this drift?
Is there a way to set up an ajax call lets say every 10-30-60 sec or so that would sync the js timer with the actual time from the server?
In theory, I’m thinking it should be possible to make an ajax call every 30 sec or so to a php file that returns the time() and then pass that to js setinterval() which in turn would do its thing and countdown for the next 30 sec after which the new synced time gets passed on again from the ajax call?
Is there a better way to achieve this while keeping accurate time?

Any input is greatly appreciated.

Thank you in advance…

So this is what I was able to put together…
Let me know what you guys think.

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Ubuntu'/>
<style>
.cd-tab {width: 160px; height:15px; line-height: 15px; background: none repeat scroll 0 0 #DCE1E9;text-align:center;padding: 5px;}
.cd-intab {width: 40px; font-family: 'Ubuntu', sans-serif; font-size: 55%; color:#666; text-align:center; height:15px; float:left}
.countdown {width: 160px; font-family: 'Ubuntu', sans-serif; font-size: 135%; color:#3D6789; text-align:center; height:20px; line-height: 15px; background: #DCE1E9;padding: 5px;}
#wrap_timer {display: block;}
#timer {float: left;}
#sync {float: left; display: block; padding:10px;}
</style>
</head>

<body>
<div class="cd-tab">
	<div class="cd-intab">DAYS</div>
	<div class="cd-intab">HRS</div>
    <div class="cd-intab">MIN</div>
    <div class="cd-intab">SEC</div>
</div>
<div id=wrap_timer>
	<div id="timer" class="countdown"></div>
    <div id="sync"><img src="img/preloader_min.gif" /></div>
</div><br /><br />
<input type="button" value="Reload Page" onClick="window.location.reload()">
<?php
// request event time from mysql generate current time and calculate the diff
$event_time = 1424644309;
$current_time = time();
$timeLeft = $event_time-$current_time;
?>
<script type="text/javascript">

//initiate the same vars as in php with the same values
var eventtime = 1424644309;
var timeLeft = <?php echo $timeLeft?>;

//ajax call to the server to get new current time
function get_time() {
    // 1. Instantiate XHR - Start
    var xhr;
	var newtime;
    if (window.XMLHttpRequest) 
        xhr = new XMLHttpRequest();
    else if (window.ActiveXObject) 
        xhr = new ActiveXObject("Msxml2.XMLHTTP");
    else 
        throw new Error("Your browser does not support Ajax. What a shame...");
    // 1. Instantiate XHR - End

    // 2. Handle Response from Server - Start
    xhr.onreadystatechange = function () {
        if (xhr.readyState < 4)
            document.getElementById('sync').style.display = "block";
        else if (xhr.readyState === 4) {
            if (xhr.status == 200 && xhr.status < 300) 
                document.getElementById('sync').style.display = "none";
				//ajax response is the new current time
				var newtime = xhr.responseText;
				//calc new diff and how many total seconds are left
				var timeLeft = eventtime - newtime;
        }
    }
    // 2. Handle Response from Server - End

    // 3. Specify your action, location and Send to the server - Start    
    xhr.open('POST', 'servertime.php');
    xhr.send(null);
    // 3. Specify your action, location and Send to the server - End
}

//pad digits with extra 0 if below 10
function pad(value) {
    if(value < 10) {
        return '0' + value;
    } else {
        return value;
    }
}

//countdown
var timer = setInterval(function() {
    timeLeft--;
	var hrs = 3600;
	var mins = 60;
	var sec = 1;
	
	/*only min and sec
    var minutesLeft = Math.floor(timeLeft / 60);
    var secondsLeft = timeLeft % 60;
	document.getElementById('timer').innerHTML = "00 : 00 : " + pad(minutesLeft) + " : " + pad(secondsLeft);
    console.log('Time left: '00 : 00 : ' + ':' + minLeft + ':' + secLeft);
	*/
	
	//hrs, min and sec
	var hrsLeft = Math.floor(timeLeft / hrs);
	var minLeft = Math.floor((timeLeft % hrs) / mins);
	var secLeft = Math.floor(timeLeft % mins);
	document.getElementById('timer').innerHTML = "00 : " + pad(hrsLeft) + " : " + pad(minLeft) + " : " + pad(secLeft);
	console.log('Time left: ' + pad(hrsLeft) + ':' + pad(minLeft) + ':' + pad(secLeft));
	
	
    if (timeLeft <= 0) {
        clearInterval(timer);
		clearInterval(mytime);
		document.getElementById('timer').innerHTML = "Ended!";
    }
}, 1000);

function synctime(){
var refresh=10000; // Refresh rate in milliseconds
mytime=setInterval('get_time();',refresh)
}

var sync = synctime();
</script>
</body>
</html>

and this is the servertime.php file

<?php
$time = time();
echo $time;
?>

Saying “Your browser does not support Ajax. What a shame…” seems a bit cruel.
If the ajax feature is not available then the least you can do is to show what information you can, that being how much time there is until the event as of the page loading without the countdown.

This is a good opportunity to remove your custom ajax solution and replace it with a known plugin. MicroAjax is only 841 bytes, which is much smaller than the 1140 bytes that you currently use.

It’s as easy as:

<script type="text/javascript" src="lib/microajax.minified.js"></script>

followed by:

document.getElementById('sync').style.display = "block";
microAjax('servertime.php', function (newtime) {
    document.getElementById('sync').style.display = "none";
    timeLeft = eventtime - newtime;
});

You might also want to hide the sync icon at the start, for it remains onscreen for the initial 10 seconds when nothing is being done.

There’s other advice that can be given, but what do you think of this lot to start with?

Hey Paul,

thanks so much for actually taking the time to test the code and for all your input (all valid points)… by all means please keep it coming.

To clarify some of the things you mentioned:
The message displayed if ajax is not supported was more of a joke something that I just typed in the spur of the moment.
I can see how that can be considered silly or even offensive for someone that has a browser that does not support that feature.
I was actually thinking of starting with a function to test for ajax support and if there is none to just display the time left from php… kind of a static countdown timer that would update only on page refresh.

My whole ajax/javascript experience is almost nonexistent as I usually just look up small snippets that help me do whatever I want to do.
This is the first ever script that I have actually came up with from start to finish, albeit I still had to research how to make an ajax call, how to get the response and all that.
Usually I try to stay away from js libraries, like jquery and the likes because to me if you only need a small portion of the functionality there’s no need to load some bloated code that you’ll never use.
I can see the advantage with MicroAjax though… btw… never heard of it before so thank you for pointing that out.
The sync div, I just forgot to hide it initially as the ajax call happens so fast (<10 milisec) that you barely ever get to see it toggled. I switched it to display: block initially to actually be able to take a look at it and see if it shows.
Forgot to hide it before I posted the code here.

As I said… complete noob here when it comes to js… so if there’s anything else you can point out that I can improve please do so…

Okay, some other improvements.

The PHP code currently has $event_time defined, but you’re not really doing much of anything with it. You can do without the PHP code in the page completely, by running get_time when the page first starts.

Remove PHP code

With the PHP code removed, you can do the following:

var eventtime = 1424744309;
var timeLeft;

synctime();
get_time();

Fix setInterval

The setInterval part can do with an update too. It’s not supposed to have a string given to it. Instead, the function name is what should be used with setInterval.

mytime=setInterval(get_time, refresh)

Hidden CSS class

Elsewhere with updating style.display - it’s preferred to instead update the class attribute as that’s less invasive and results being able to update just the CSS later on to achieve different types of display choices.

We can start off by extracting the display:none out to a separate CSS class, and apply that to the preloader:

#sync {float: left; padding:10px;}
.hidden {display: none;}

Now we can update the JavaScript so that instead of digging in to the CSS styles, it only updates the element class instead:

document.getElementById('sync').classList.remove('hidden');
...
document.getElementById('sync').classList.add('hidden');

And because we are running get_time() right away, it remains hidden when the page loads too.

After those improvements there are some structural issues that can be looked at next.

After a few more updates we have some nicely cleaned up code.

The immediately invoked function expression provides a nicely sandboxed area for the code to run, protected from issues such as global scope pollution.

(function () {
    ...
}());

The variables are defined together at the top of the function.

There is potential trouble with setInterval if the initial request takes longer than a second to respond, so updating the time occurs only when the remaining time is known.

function get_time() {
    ...
    if (!timer) {
        timer = setInterval(updateCountdown, 1000);
    }
}

The pad function works perfectly well, but since its behaviour is so well known is it worthwhile to condense things there somewhat using the following ternary selector?

function pad(value) {
    return (value < 10 ? '0' : '') + value;
}

The rest of the code such as in updateCountdown I’ve left mostly alone, because if I start to work in there I’ll end up replacing it all with Date().toTimeString methods to automatically create formatted times.

The following isn’t used, but it’s one possible way to retrieve the remaining time from a Date object:

var date = new Date(),
    remainingTime;
date.setTime(timeLeft * 1000);
remainingTime = date.toUTCString().match(/\d+:\d+:\d+/)[0],

I’ve also renamed synctime to ‘initTime’ as it kicks off the timer processes, which means that the end of the script we just have the following, to get the time left and init the timer.

getTime();
initTime();

The above leaves us now with the following code:

(function () {
    'use strict';

    var eventtime = 1424744309,
        timeLeft = 0,
        timer,
        mytime;

    function pad(value) {
        return (value < 10 ? '0' : '') + value;
    }

    function updateCountdown() {
        timeLeft -= 1;
        var hrs = 3600,
            mins = 60,
            hrsLeft = Math.floor(timeLeft / hrs),
            minLeft = Math.floor((timeLeft % hrs) / mins),
            secLeft = Math.floor(timeLeft % mins);

        document.getElementById('timer').innerHTML = "00 : " + pad(hrsLeft) + " : " + pad(minLeft) + " : " + pad(secLeft);

        if (timeLeft <= 0) {
            clearInterval(timer);
            clearInterval(mytime);
            document.getElementById('timer').innerHTML = "Ended!";
        }
    }

    function getTime() {
        document.getElementById('sync').classList.remove('hidden');
        microAjax('servertime.php', function (newtime) {
            document.getElementById('sync').classList.add('hidden');
            timeLeft = eventtime - newtime;
        });
        if (!timer) {
            timer = setInterval(updateCountdown, 1000);
        }
    }

    function initTime() {
        var refresh_ms = 10000;
        mytime = setInterval(getTime, refresh_ms);
    }

    getTime();
    initTime();
}());

Other improvements have been to tun the code through jslint.com to weed out some bad practices.

Other improvements can be made too so that it’s more appropriately structured and capable of handling future updates more easily, but I think that this is a good start.

Oh and by the way, are there any places that you would want to add comments? Because other than a comment block at the start to give an overall description, most other types of comments throughout the code can be easily avoided by putting things in to well-named functions, or by structuring the code so-as to make things easier to understand.

wow… a lot to take in…
I shall report back as soon as I digest everything :smile:

Couple of things that I wanna mention are…

the reason for the $event_time from php is, that this will be a constant pulled from mysql…
and the $current_time is the time() from the server at the instance of page loading.
thus… timeLeft is instantly defined from the get go… and can already start the countdown.
After which, every 10 sec, update the timeLeft with the ajax call.
I wanted to do all this initially so that I don’t have to deal with js’ new Date() and have to account for user’s time zone whether or not their local time is synchronized, DST and all that.
Instead I wanted to constantly get the remaining time in seconds straight from the server… and just perform a simple countdown in between calls.

At a first glance, I already fell in love with your pad function, since that replaces 4 lines of code I wrote, in a single 1.
Didn’t even know you could do that…
Guess my programming skills are a bit dated and should look more into new practices…

Again, thx so much for your patience and for taking the time to get back to me

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.