<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 !
Glen Solsberry
June 27, 2008, June 27, 2008 21:06, permalink
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>
Ryan Cooke
June 29, 2008, June 29, 2008 21:32, permalink
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>
Chris Jester-Young
June 29, 2008, June 29, 2008 22:23, permalink
@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();
};
}
Ryan Cooke
July 3, 2008, July 03, 2008 04:10, permalink
@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);
}
Tien Dung
July 24, 2008, July 24, 2008 01:03, permalink
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>
Tien Dung
July 24, 2008, July 24, 2008 01:12, permalink
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>
steve
July 28, 2008, July 28, 2008 12:39, permalink
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!
Tien Dung
July 28, 2008, July 28, 2008 23:43, permalink
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.
AnonymousBot
August 1, 2008, August 01, 2008 20:22, permalink
Change line 12 to read:
return Math.floor(x/60) + ":" + (x<10?"0":"")+x%60;
AnonymousBot
August 1, 2008, August 01, 2008 20:45, permalink
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;
some
November 17, 2008, November 17, 2008 11:06, permalink
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"
}
);
seaofclouds
January 2, 2009, January 02, 2009 21:54, permalink
@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.
seaofclouds
January 3, 2009, January 03, 2009 04:24, permalink
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}"
}
);
StevenMc
February 6, 2009, February 06, 2009 11:36, permalink
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>
JavaScript Countdown Timer
October 4, 2009, October 04, 2009 16:16, permalink
very cool & good tip, thank you very much for sharing.
Awaiting your response. Thank
Erickson
October 30, 2010, October 30, 2010 07:37, permalink
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>
sriram
December 22, 2011, December 22, 2011 22:55, permalink
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>
Daniel
February 7, 2012, February 07, 2012 07:04, permalink
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?
Daniel
February 7, 2012, February 07, 2012 07:05, permalink
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?
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.