79b4825afe5c22c7d6226037ac81ee69

Hello Everyone.
I'm trying to create a countdown timer in javascript but it's a bit cluttered. Ideally I would like it to be unobtrusive in Prototype.

<div id="countdown"></div>
<div id="notifier"></div>

<script type="text/javascript">

  var start = 20;
  Number.prototype.toMinutesAndSeconds = function() {
	var nbr = Math.floor(this / 60);
	return (nbr+":")+(((nbr=(this-(nbr*60)))<10)?"0"+nbr:nbr);
}

function display(seconds, output) {
	output.innerHTML = (--seconds).toMinutesAndSeconds();
	if(seconds > 0) {
		window.setTimeout(function(){display(seconds, output)}, 1000);
	}
	
	if(seconds < 11) {
		document.getElementById("notifier").innerHTML = "Just 10 seconds to go";
	}
	
	if (seconds == 0) {
		document.getElementById("notifier").innerHTML = "Time is up baby";
	}
}
display(start, document.getElementById("countdown"));

</script>

Refactorings

No refactoring yet !

9d8de42cebeeaf6d223d86bb9130832b

Glen Solsberry

June 27, 2008, June 27, 2008 21:06, permalink

No rating. Login to rate!

What about the following?

<div id="countdown"></div>
<div id="notifier"></div>

<script type="text/javascript">

  var start = 20;
  Number.prototype.toMinutesAndSeconds = function() {
	var m = Math.floor(this / 60);
	var s = this % m;
	//return (nbr+":")+(((nbr=(this-(nbr*60)))<10)?"0"+nbr:nbr);
	return (nbr+":"+(s<10?"0":"")+s);
}

function display(seconds, output) {
	output.innerHTML = (--seconds).toMinutesAndSeconds();
	if(seconds > 0) {
		window.setTimeout(function(){display(seconds, output)}, 1000);
	}
	
	var notifier = document.getElementById("notifier");
	if(seconds < 11) {
		notifier.innerHTML = "Just 10 seconds to go";
	}
	
	if (seconds == 0) {
		notifier.innerHTML = "Time is up baby";
	}
}
display(start, document.getElementById("countdown"));

</script>
Ee6004d792f0e95649571bfe483daf65

Ryan Cooke

June 29, 2008, June 29, 2008 21:32, permalink

1 rating. Login to rate!

Here's an OO implementation. Theres a contextual assignment in _TDisplay.prototype.tick that might seem backwards to you if you aren't familiar with the prototype model.

Above refactoring had a couple of NaN bugs in Number.prototype.toMinutesAndSeconds that I also fixed.

<div id="countdown"></div>
<div id="notifier"></div>

<script type="text/javascript">

Number.prototype.toMinutesAndSeconds = function() {
	var m = Math.floor(this / 60);
	var s = this % 60;
	return (m+":"+(s<10?"0":"")+s);
}

function _TDisplay() {}
_TDisplay.prototype.init = function(seconds, output, notify) {
	this.tot = seconds;
	this.out = output;
	this.not = notify;
	this.ii = setInterval(this.tick, 1000);
};
_TDisplay.prototype._tick = function() {
    this.out.innerHTML = (--this.tot).toMinutesAndSeconds();
    if(!this.tot) {
    	this.not.innerHTML = 'Time is up baby';
    	clearInterval(this.ii);
    }
    
    if(this.tot == 10) {
    	this.not.innerHTML = 'Just 10 seconds to go';
    }
}
TDisplay = new _TDisplay();
_TDisplay.prototype.tick = function() {
	TDisplay._tick.call(TDisplay);
}



/*using Prototype framework?
Event.observe(window, 'onload', function() {
	TDisplay.init(20, document.getElementById('countdown'), document.getElementById('notifier'));
});
*/

/*using mootools framework?
window.addEvent('domready', function() {
	TDisplay.init(20, document.getElementById('countdown'), document.getElementById('notifier'));
});
*/
	
//otherwise
pre = (window.onload) ? window.onload : function () {};
window.onload = function () {
	pre();
	TDisplay.init(20, document.getElementById('countdown'), document.getElementById('notifier'));
}

</script>
729442eea8d8548842a6e0947e333c7b

Chris Jester-Young

June 29, 2008, June 29, 2008 22:23, permalink

No rating. Login to rate!

@Ryan: Yes, it seems backwards to me---why would a "prototype" function refer to a specific instance of _TDisplay? Is it meant to model the singleton pattern? A not-so-singleton approach would be to assign the closure to "TDisplay.tick", not "_TDisplay.prototype.tick". (I can understand the use of "TDisplay._tick.call(TDisplay)", because the callback from "setInterval" loses the "this" reference.)

Of course, a not-so-singleton approach would also assign the "tick" property in the constructor, rather than assigning the "tick" property in "global" code. :-) Something like the code box below.

(ETA: My original version didn't take into account that the "this" reference might not be saved as part of the closure's environment. This will sort that out. Also, calling self._tick() will automatically set the "this" reference correctly, no need to use call().)

function _TDisplay() {
    var self = this;
    this.tick = function () {
        self._tick();
    };
}
5997d626098b88721c9393c68f633fc1

Ryan Cooke

July 3, 2008, July 03, 2008 04:10, permalink

No rating. Login to rate!

@Chris: I used my prototyping method mostly because closures leak memory in ie. Depending on the usage, they can be debilitating in many seemingly-benign code blocks, so I try to avoid them at all costs. Its also borrowing from the idea of programming to an interface, but you are correct to point out the singleton constraint of my "_TDisplay.prototype.tick" assignment. I've corrected that below. This will let you create as many closure-free timers as you want, and you can think of it as implementing the _TDisplay "interface" and utilizing javascript's wonderful ability of execution in arbitrary contexts.

TDisplay = new _TDisplay();
TDisplay.tick = function() {
	TDisplay._tick.call(TDisplay);
}
5071c5b861341c0dcfcf6ac86327701f

Tien Dung

July 24, 2008, July 24, 2008 01:03, permalink

No rating. Login to rate!

Here is my refactor code. Make use of JavaScript idioms and functional style. Also use module pattern (function (){})(); to reduce conflict.

<html>
<body>
<div id="countdown"></div>
<div id="notifier"></div>

<script type="text/javascript">

(function () {
  function display( notifier, str ) {
    document.getElementById(notifier).innerHTML = str;
  }
	
  function toMinuteAndSecond( x ) {
    return Math.floor(x/60) + ":" + (x=x%60 < 10 ? 0 : x);
  }
	
  function setTimer( remain, actions ) {
    var action;
    (function countdown() {
       display("countdown", toMinuteAndSecond(remain));		
       if (action = actions[remain]) {
         action();
       }
       if (remain > 0) {
         remain -= 1;
         setTimeout(arguments.callee, 1000);
       }
    })(); // End countdown
  }

  setTimer(20, {
    10: function () { display("notifier", "Just 10 seconds to go"); },
     5: function () { display("notifier", "5 seconds left");        },
     0: function () { display("notifier", "Time is up baby");       }
  });	
})();

</script>
</body>
</html>
5071c5b861341c0dcfcf6ac86327701f

Tien Dung

July 24, 2008, July 24, 2008 01:12, permalink

2 ratings. Login to rate!

Compact version

<html>
<body>
<div id="countdown"></div>
<div id="notifier"></div>
<script type="text/javascript">

  function display( notifier, str ) {
    document.getElementById(notifier).innerHTML = str;
  }
	
  function toMinuteAndSecond( x ) {
    return Math.floor(x/60) + ":" + x%60;
  }
	
  function setTimer( remain, actions ) {
    (function countdown() {
       display("countdown", toMinuteAndSecond(remain));		
       actions[remain] && actions[remain]();
       (remain -= 1) >= 0 && setTimeout(arguments.callee, 1000);
    })();
  }

  setTimer(20, {
    10: function () { display("notifier", "Just 10 seconds to go"); },
     5: function () { display("notifier", "5 seconds left");        },
     0: function () { display("notifier", "Time is up baby");       }
  });	

</script>
</body>
</html>
79b4825afe5c22c7d6226037ac81ee69

Jermaine

July 24, 2008, July 24, 2008 06:50, permalink

No rating. Login to rate!

Guys, this helped me alot. Thanks so much!

4592f7edcb3d4acb437774c4e2d784c1

steve

July 28, 2008, July 28, 2008 12:39, permalink

No rating. Login to rate!

guys - awesome script. great resource for code idiots like myself. one question...on this last compact version Tien - when the timer gets to the last 10 seconds of any minute (setting for multiple minutes) it only displays the "0" and not the other number after. i.e. the last ten seconds of the 42nd minute show "42:0", until it hits "39:59" and continues. Any idea how to fix that? Thanks!

5071c5b861341c0dcfcf6ac86327701f

Tien Dung

July 28, 2008, July 28, 2008 23:43, permalink

No rating. Login to rate!

Hi Steve, just change to function display to Math.floor(x/60) + ":" + x%60;

I changed the compact version in case you need full example.

C510febb9bed68b5cc4a09f076701e0f

AnonymousBot

August 1, 2008, August 01, 2008 20:22, permalink

No rating. Login to rate!

Change line 12 to read:

return Math.floor(x/60) + ":" + (x<10?"0":"")+x%60;
C510febb9bed68b5cc4a09f076701e0f

AnonymousBot

August 1, 2008, August 01, 2008 20:45, permalink

No rating. Login to rate!

Sorry, one slight correction. The above only worked for seconds under 10 (meaning 0:10 and below). This should work for all seconds under 10, regardless of the minute. Make the change below to line 12 of Tien Dungs compact version code.

return Math.floor(x/60) + ":" + (x%60<10?"0":"")+x%60;
Ae5de8fa45f79815c64b98690088a48d

some

November 17, 2008, November 17, 2008 11:06, permalink

1 rating. Login to rate!

I like Tien Dungs approach with actions, since it is more generic.
But I don't like that the element is retrieved from the DOM every time it is used, neither is using setTimeout when it is an interval since that makes the timer drift. In this example it doesn't matter, but if it is a big document and the timer is running for a longer time (on a slow computer) it does.

Here is my minimalistic approach:

function countdown(remain,messages) {
	var
		notifier = document.getElementById("notifier"),
		countdown = document.getElementById("countdown"),
		timer = setInterval( function () {
			countdown.innerHTML = Math.floor(remain/60) + ":" + (remain%60 < 10 ? "0": "") + remain %60;
			if (messages[remain]) { notifier.innerHTML = messages[remain]; }
			if (--remain < 0 ) { clearInterval(timer); }
		},1000);
}


countdown(20,
	{ 
		10: "Just 10 seconds to go",
		5: "5 seconds left",
		0: "Time is up baby"
	}
);
F55e8e113669f6ea7d1d99f38907ce54

seaofclouds

January 2, 2009, January 02, 2009 21:54, permalink

No rating. Login to rate!

@some - real nice solution. i wonder if you could help me extend your solution to include larger time increments, such as:

1 year, 72 days left until blank.

F55e8e113669f6ea7d1d99f38907ce54

seaofclouds

January 3, 2009, January 03, 2009 04:24, permalink

No rating. Login to rate!

alright, i've got it working with days and such - much easier to figure out than i thought it would be. my code, seen here, has some ruby bits scattered throughout that are part of my bigger sinatra app which you can play with at: http://github.com/seaofclouds/is-it-blank-yet

var countdown_d = new Date();
      var countdown_timenow = countdown_d.getTime();
      var countdown_targetdate = Date.parse("#{params[:year]}/#{params[:month]}/#{params[:day]} #{params[:hour]}:#{params[:minute]}")
      var countdown_timeleft = Math.floor((countdown_targetdate-countdown_timenow)/1000);
      function countdown(remain,messages) {
        var
          status = document.getElementById("status"),
          notifier = document.getElementById("notifier"),
          countdown = document.getElementById("countdown"),
          timer = setInterval( function () {
            var day = (Math.floor(remain/86400))%86400;
            var hour = (Math.floor(remain/3600))%24;
            var minute = (Math.floor(remain/60))%60;
            var second = (Math.floor(remain/1))%60;
            countdown.innerHTML = (day > 0 ? "<span class='countdown_day'><strong>"+day+"</strong> days</span> " : "") + (hour > 0 ? "<span class='countdown_hour'><strong>"+hour+"</strong> hours</span> " : "") + (minute > 0 ? "<span class='countdown_minute'><strong>"+minute+"</strong> minutes</span> " : "") + (second > 0 ? "<span class='countdown_second'><strong>"+second+"</strong> seconds</span> " : "")
            if (--remain < 0 ) { 
              clearInterval(timer); 
              document.body.id = "yes"; 
              status.innerHTML = "yes"; 
              notifier.innerHTML = "it is <strong>#{@name}</strong>"
            } else {
              document.body.id = "no"; 
              status.innerHTML = "no"; 
              notifier.innerHTML = "it is not <strong>#{@name}</strong>, but it will be in"
            }
          },1000);          
      }
      countdown(countdown_timeleft,
        { 
          86400: "it will be #{@name} tomorrow.",
          10: "it will be #{@name} in ten seconds",
          0: "it is #{@name}"
        }
      );
Df637328cb39713a0e3a28b6277618cd

StevenMc

February 6, 2009, February 06, 2009 11:36, permalink

No rating. Login to rate!

I love this solution. Unfortunately it didn't quite do what I needed. I wanted the ability to pass a "reset" parameter to the function. Now, this isn't exactly making your program more efficient, but with a pretty easy adjustment it's now got more function. I added a parameter to the countdown function, added a one line if statement at the start and removed the comma after the countdown var declaration. Finally I added a var timer; before the function to make it global.

var timer;
function countdown(remain,messages, reset){
	if (reset == true)clearInterval(timer);	
	var
		notifier = document.getElementById("notifier"),
		countdown = document.getElementById("countdown")
		timer = setInterval( function () {
			countdown.innerHTML = Math.floor(remain/60) + ":" + (remain%60 < 10 ? "0": "") + remain %60;
			if (messages[remain]) { notifier.innerHTML = messages[remain]; }
			if (--remain < 0 ) { clearInterval(timer); alert("time up");}
		},1000);
}

// this can now be reset with: <a href="javascript:countdown(1500, '', true);">reset</a>
4c755e69e5f1fac6cc22c090615f6fea

JavaScript Countdown Timer

October 4, 2009, October 04, 2009 16:16, permalink

No rating. Login to rate!

very cool & good tip, thank you very much for sharing.
Awaiting your response. Thank

E177c7f266a8ad07520ff5237678216b

JB

July 4, 2010, July 04, 2010 20:57, permalink

No rating. Login to rate!

Just used this in my solution.. Thanks guys!

269afe2021aa3a60d934eca7b4affcd4

Erickson

October 30, 2010, October 30, 2010 07:37, permalink

No rating. Login to rate!

Your codes here really rocks! it took me 2 hours just to have this code and thanks God I found here. cheers!

<script type="text/javascript">
var page = "page-3.html"; //The page to redirect to
  function display( notifier, str ) {
    document.getElementById(notifier).innerHTML = str;
  }
	
  function toMinuteAndSecond( x ) {
    return Math.floor(x/60) + ":" + x%60;
  }
	
  function setTimer( remain, actions ) {
    (function countdown() {
       display("countdown", toMinuteAndSecond(remain));		
       actions[remain] && actions[remain]();
       (remain -= 1) >= 0 && setTimeout(arguments.callee, 1000);
	       if(remain == -1){
			window.location = page;
}

    })();
  }

  setTimer(120,0);	

</script>
D41d8cd98f00b204e9800998ecf8427e

sriram

December 22, 2011, December 22, 2011 22:55, permalink

No rating. Login to rate!

but ...the timer is restarting when the page is refreshed ...what to do if in case i use it for a quiz site

<div id="countdown"></div> <div id="notifier"></div> <script type="text/javascript"> var start = 20; Number.prototype.toMinutesAndSeconds = function() { var nbr = Math.floor(this / 60); return (nbr+":")+(((nbr=(this-(nbr*60)))<10)?"0"+nbr:nbr); } function display(seconds, output) { output.innerHTML = (--seconds).toMinutesAndSeconds(); if(seconds > 0) { window.setTimeout(function(){display(seconds, output)}, 1000); } if(seconds < 11) { document.getElementById("notifier").innerHTML = "Just 10 seconds to go"; } if (seconds == 0) { document.getElementById("notifier").innerHTML = "Time is up baby"; } } display(start, document.getElementById("countdown")); </script>
0d89ce7dd51f1cd304d2141e8290c5cf

Daniel

February 7, 2012, February 07, 2012 07:04, permalink

No rating. Login to rate!

I have this changed around a bit but doe's anybody know how I could have an image display on the count of five and then hidden on zero?

0d89ce7dd51f1cd304d2141e8290c5cf

Daniel

February 7, 2012, February 07, 2012 07:05, permalink

No rating. Login to rate!

I have this changed around a bit but doe's anybody know how I could have an image display on the count of five and then hidden on zero?

Your refactoring





Format Copy from initial code

or Cancel