2
\$\begingroup\$

I've written this little countdown script to count until the end of our school days.

Now, that was written quickly, badly remembering modulo from a few years ago, and I don't think that's very optimized.

I thought about updating only the seconds and check if it needs to update the minute too, etc. But it wouldn't be accurate as setTimeout is not (depending if the browser lags, etc).

(function() {
    //the variable block here seems kinda weird, but whatever toots your horn
    var el = document.getElementById('countdown'),
        endDate = new Date('March 30, 2012 18:10:00'),
        curDate,
        diff,
        days,
        hours,
        minutes,
        seconds,
        tmp,
        countdown,
        //added Math.floor. this is already a shortcut, might as well make it a double one
        pad = function(number) { return (number < 10 ? '0' : '') + Math.floor(number) },

        //calculate these constants once, instead of over and over
        minute = 60 * 1000,
        hour = minute * 60,
        day = hour * 24

    ;(function tick() {
        curDate = new Date()
        //you want the absolute value of this, not of individual calculations using this
        diff = Math.abs(new Date(curDate.getTime() - endDate.getTime()))
        days = diff / day

        tmp = diff % day
        hours = tmp / hour

        tmp = tmp % hour
        minutes = tmp / minute

        tmp = tmp % minute
        seconds = tmp / 1000

        //parseInt was redundant
        countdown = pad(days) + ':' + pad(hours) + ':' + pad(minutes) + ':' + pad(seconds)

        if ( 'textContent' in el ) {
            el.textContent = countdown
        }
        else {
            el.innerText = countdown
        }

        //dont't use arguments.calle, it's deprecated
        //simply use a named function expression
        setTimeout(tick, 1000)
    }())
}())​

About the semicolons missing: it is a deliberate choice as I find this more readable (the ; right before the (function() is because it is the only edge case where the semicolon insertion doesn't work correctly).

\$\endgroup\$
5
  • \$\begingroup\$ Some cool stuff going on here, never thoughts of using arguments.callee like that to avoid a named function. though mdn does seem to recommend against it: developer.mozilla.org/en/JavaScript/Reference/… \$\endgroup\$ Commented Mar 23, 2012 at 17:19
  • \$\begingroup\$ Yep, it is deprecated :-). I just posted a reviewed version by @Zirak. Not sure if I should close the question or not. \$\endgroup\$ Commented Mar 23, 2012 at 17:24
  • 1
    \$\begingroup\$ you wanan get really slick? el[('textContent' in el) ? 'textContent' : 'innerText'] = countdown Also though, you can cache the result of that calculation \$\endgroup\$ Commented Mar 23, 2012 at 19:24
  • \$\begingroup\$ Good point, I should definitely cache the result of this. \$\endgroup\$ Commented Mar 24, 2012 at 8:27
  • \$\begingroup\$ setInterval() is far more optimized and is often recommended over setTimeout(). There is an awesome article by John Resig (inventor of jQuery and co-author of Secrets of the JavaScript Ninja). The book is amazing, I recommend it to everyone too! John Resig's blog \$\endgroup\$
    – user59639
    Commented Dec 11, 2014 at 8:06

2 Answers 2

1
\$\begingroup\$

Your whole code can be reduced to this.

/* secsToDHMS()
 * Author Gary Green
 * http://stackoverflow.com/users/63523/gary-green
 */
function secsToDHMS(s)
{
  var x = [86400, 3600, 60, 1],z,i=-1;
  while (z=x[++i])
        x[i] = ("0" + parseInt(s / z,10)).slice(-2),
        s %= z;

    return x.join(':');
};

(function() {

    var currentDate      = new Date();
    var endDate          = new Date('March 30, 2012 18:10:00');
    var secsToGo         = (new Date(endDate - currentDate)).getTime() / 1000;
    var countdownElement = document.getElementById('countdown');

    var timer = setInterval(function() {

        if (--secsToGo < 0) {
            clearInterval(timer);
            return;
        }

        countdownElement.innerHTML = secsToDHMS(secsToGo);

    }, 1000);

})();

Fiddle: http://jsfiddle.net/xg63V/4


Compressed version 311 bytes

(function(){var d=(new Date(new Date("March 30, 2012 18:10:00")-new Date)).getTime()/1E3,
f=document.getElementById("countdown"),g=setInterval(function(){if(0>--d)clearInterval(g);
else{for(var a=d,b=[86400,3600,60,1],c,e=-1;c=b[++e];)b[e]=("0"+parseInt(a/c,10)).slice(-2),
a%=c;f.innerHTML=b.join(":")}},1E3)})();​
\$\endgroup\$
1
  • \$\begingroup\$ Your version looks somehow nice, but I don't like the use of setInterval or innerHTML. The secsToDHMS function is really nice though! \$\endgroup\$ Commented Mar 24, 2012 at 8:25
0
\$\begingroup\$

First question - your countdown is "symmetric", it does not care if current date is past the end date. Is it on purpose?

Define constants to avoid "magic numbers": var ONE_DAY = 1000 * 60 * 60 * 24 etc

May be it is better to calculate diff = Math.abs(curDate.getTime() - endDate.getTime()) without new object construction, and you do not need to invoke Math.abs anymore (see the question above).

Then, days = Math.floor(diff / ONE_DAY) to ensure integer number. Same for other calculations.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your advices, they're already used in @Zirak's version :). The symmetric part of the countdown is okay, a simple if check will solve this. \$\endgroup\$ Commented Mar 24, 2012 at 8:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.