1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
<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?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
<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().)
1 2 3 4 5 6
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.
1 2 3 4
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
<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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<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:
1
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.
1
return Math.floor(x/60) + ":" + (x%60<10?"0":"")+x%60;
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.