Javascript adding a pause option to countdown timer not working - javascript

I have a javascript function to countdown a timer. So I want to add pause option to this function. I tried this way,
function countdownTimeStart() {
var el = document.getElementById('demo');
var pause= document.getElementById('pause');
var time = [10,10,10];
var x = setInterval(function () {
var hours = time[0];
var minutes = time[1];
var seconds = time[2]--;
if (time[2] == -1) {
time[1]--;
time[2] = 59 }
function pauseTimer() {
savedTime = time;
clearInterval(x);
}
pause.addEventListener( 'click', pauseTimer);
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval(x);
el.innerHTML = "00:00:00";
} else if (seconds < 10) {
el.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
el.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}, 1000);
}
countdownTimeStart();
<button id="pause" class="pause">Pause</button>
<div id="demo"></div>
The countdown timer working correctly. But the pause option not working. So how can I correct this script. Can someone help me.

Although your code is working, I would like to note a couple of things: adding your pause handler inside your interval isn't a good idea, you will be adding a pause handler every interval, so in the end you are just stacking up the amount of functions to handle when clicking. I have made your button toggle and separated out the event listener into a handler function so you can attach it to any button. These changes will keep your code working fluently while also making it easier to understand:
function initCountdown() {
function event_click( event ){
// If our interval is null, we need to start the counter
// And also change the innerText so its obvious what the button will do next
if( interval === null ){
start();
event.target.innerText = 'pause';
} else {
pause();
event.target.innerText = 'start';
}
}
function start(){
// First use pause() to be sure all intervals are cleared
// it prevents them from doubling up
pause();
interval = setInterval( count, 1000 );
}
function pause() {
clearInterval( interval );
interval = null;
}
function count(){
// By doing this before declaring your variables
// you make it so the variables actually hold the new calculated values.
time[2]--;
if( time[2] == -1 ){
time[1]--;
time[2] = 59;
}
// Lets use some cool new syntax here to reduce the amount of code needed
// this will destructure an array assigning their indexed values to the index of the variable
var [ hours, minutes, seconds ] = time;
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval( interval );
}
// We always want to print something, and if the values are 0
// the output is still the same, so lets seperate that.
if (seconds < 10) {
outputElement.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
outputElement.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}
// Lets also clearly name our things.
var outputElement = document.getElementById('demo');
var toggleElement = document.getElementById('toggle');
var interval = null;
var time = [10,10,10];
// Add event listener once
toggleElement.addEventListener( 'click', event_click );
toggleElement.click();
}
initCountdown();
<button id="toggle">start</button>
<div id="demo"></div>
Update Adding a cancel button:
function initCountdown() {
function event_click_cancel( event ){
pause();
time = [ 0, 0, 0 ];
print();
}
function event_click_startpause( event ){
// If our interval is null, we need to start the counter
// And also change the innerText so its obvious what the button will do next
if( interval === null ){
start();
event.target.innerText = 'pause';
} else {
pause();
event.target.innerText = 'start';
}
}
function start(){
// First use pause() to be sure all intervals are cleared
// it prevents them from doubling up
pause();
interval = setInterval( count, 1000 );
}
function pause() {
clearInterval( interval );
interval = null;
}
function print(){
// I have separated out the print function as we want to use it
// in the count and the cancel function
var [ hours, minutes, seconds ] = time;
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval( interval );
}
if (seconds < 10) {
outputElement.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
outputElement.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}
function count(){
// By doing this before declaring your variables
// you make it so the variables actually hold the new calculated values.
time[2]--;
if( time[2] == -1 ){
time[1]--;
time[2] = 59;
}
print();
}
// Lets also clearly name our things.
var outputElement = document.getElementById('demo');
var toggleElement = document.getElementById('toggle');
var cancelElement = document.getElementById('cancel');
var interval = null;
var time = [10,10,10];
// Add event listener once
toggleElement.addEventListener( 'click', event_click_startpause );
toggleElement.click();
cancelElement.addEventListener( 'click', event_click_cancel );
}
initCountdown();
<button id="toggle">start</button>
<button id="cancel">cancel</button>
<div id="demo"></div>

Related

How to add a pause/play function to a running setInterval?

So I am trying to create a pomodoro timer. The website currently looks and functions like this: POMODOROone
My timer is counting down correctly so far. However, when I try to add a pause and resume functionality, it breaks. I have tried multiple ways but I haven't had much success. So I removed all my attempts and I have the code for what is working so far.
So, my question is how would I add a pause/resume functionality to my timer? Also, how would I make my timer stop when I click on any of the positive and negative spans or the inputs?
Here is the Javascript:
//decreases break time by 5 mins
function decreaseBreak() {
var time = document.getElementById("breakInput").value;
time = parseInt(time, 10);
var new_time = time - 5;
if (time == 0) {
return 0;
}
document.getElementById("breakInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
}
//increases break time by 5 mins
function increaseBreak() {
var time = document.getElementById("breakInput").value;
time = parseInt(time, 10);
var new_time = time + 5;
document.getElementById("breakInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
}
//decreases session time by 5 mins
function decreaseSession() {
var time = document.getElementById("sessionInput").value;
time = parseInt(time, 10);
var new_time = time - 5;
if (time == 0) {
return 0;
}
document.getElementById("sessionInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
}
//increases session time by 5 mins
function increaseSession() {
var time = document.getElementById("sessionInput").value;
time = parseInt(time, 10);
var new_time = time + 5;
document.getElementById("sessionInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
}
//countdown timer
function start() {
var sec = 60;
var timerParagraph = document.getElementById("timerParagraph").innerHTML;
var min = timerParagraph.substring(0, timerParagraph.indexOf(":"));
var time = min * 60;
min = parseInt(min,10)-1;
setInterval(function() {
sec = sec - 1;
if (sec < 0) {
min -= 1;
sec = 59;
}
if (min < 0 && sec < 0) {
clearInterval(start());
}
var temp;
if (min.toString().length == 1 && sec.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = "0" + min + ":" + sec;
} else if (sec.toString().length == 1 && min.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = min + ":" + "0" + sec;
} else if (min.toString().length == 1 && sec.toString().length ==1) {
document.getElementById("timerParagraph").innerHTML = "0" + min + ":" + "0" + sec;
} else {
document.getElementById("timerParagraph").innerHTML = min + ":" + sec;
}
},1000);
}
New Updated Code:
//decreases break time by 5 mins
function decreaseBreak() {
var time = document.getElementById("breakInput").value;
time = parseInt(time, 10);
var new_time = time - 5;
if (time == 0) {
return 0;
}
document.getElementById("breakInput").value = new_time;
clearInterval(timer);
document.getElementById("timerParagraph").innerHTML = new_time+":00";
}
//increases break time by 5 mins
function increaseBreak() {
var time = document.getElementById("breakInput").value;
time = parseInt(time, 10);
var new_time = time + 5;
document.getElementById("breakInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
clearInterval(timer);
timer = null;
}
//decreases session time by 5 mins
function decreaseSession() {
var time = document.getElementById("sessionInput").value;
time = parseInt(time, 10);
var new_time = time - 5;
if (time == 0) {
return 0;
}
document.getElementById("sessionInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
clearInterval(timer);
}
//increases session time by 5 mins
function increaseSession() {
var time = document.getElementById("sessionInput").value;
time = parseInt(time, 10);
var new_time = time + 5;
document.getElementById("sessionInput").value = new_time;
document.getElementById("timerParagraph").innerHTML = new_time+":00";
clearInterval(timer);
}
//countdown timer
var timer = null;
var sec = 60;
function start() {
var timerParagraph = document.getElementById("timerParagraph").innerHTML;
var min = timerParagraph.substring(0, timerParagraph.indexOf(":"));
min = parseInt(min,10)-1;
function onTimer() {
sec = sec - 1;
if (sec < 0) {
min -= 1;
if (min == 0 && sec == 0) {
// When min and second equals zero, timer stops
clearInterval(timer);
}
sec = 59;
}
if (min.toString().length == 1 && sec.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = "0" + min + ":" + sec;
} else if (sec.toString().length == 1 && min.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = min + ":" + "0" + sec;
} else if (min.toString().length == 1 && sec.toString().length ==1) {
document.getElementById("timerParagraph").innerHTML = "0" + min + ":" + "0" + sec;
} else {
document.getElementById("timerParagraph").innerHTML = min + ":" + sec;
}
}
timer = setInterval(onTimer,1000);
console.log("Start timer");
// when the div is clicked, the timer starts and stops.
document.getElementById("timerDiv").onclick = function() {
if (timer) {
clearInterval(timer);
timer = null;
} else {
timer = setInterval(onTimer,1000);
console.log("Resume timer");
}
}
}
This updated code works for the following new actions:
1. When the timer starts playing, and if the timerDiv is clicked, it will pause.
2. And when clicked again, it will resume.
Now, when I click on the positive and negative signs and change the time, it changes the time like it should and clears the interval like it is called. However, when I click on the timerDiv again, instead of playing from the start, it resumes playing the time from where it left off. How would I make it so that when I click on the timerDiv again, it counts down from the new time instead of resuming previous time.
Here is an implementation called interruptible-timer:
// A timer which can be interrupted and picks up where you left off.
//
// Usage:
// ```
// var timer = interruptibleTimer(task, 5000);
// timer.start(); // Start the timer, or resume it after stopping.
// timer.stop(); // Timer continues to run, but task will not be called.
// timer.run(); // Run task now, and start timer again from now.
// timer.reset(); // Stop the timer and reset it to 0.
// ```
var set = window.setTimeout;
var clear = window.clearTimeout;
function interruptibleTimer(fn, interval) {
var recent = 0;
var timer;
// PRIVATE FUNCTIONS
function now() { return +new Date(); }
function delay() { return recent ? Math.max(0, recent + interval - now()) : interval; }
function schedule() { timer = set(run, delay()); }
// PUBLIC APIs
function start() { if (!timer) schedule(); }
function stop() { clear(timer); timer = 0; }
function run() { fn(); recent = now(); schedule(); }
function reset() { stop(); recent = 0; }
return {start, stop, run, reset};
}
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_win_setinterval_clearinterval
The way you set your interval and clear it inside of the start() function doesn't make much sense and is probably the cause of your troubles.
The function called every second should be stored with a function name.
/*
A wrapper variable is used to keep variables in scope so clearinterval can be used and variables don't interact outside of 'T'.
*/
var T = {
sec: null,
timerParagraph: null,
min: null,
time: null,
interval: null,
//countdown timer
start: function(){
T.interval = setInterval(T.tickClock,1000);
},
pause: function(){
clearInterval(T.interval);
},
reset: function(){
T.sec = 60;
T.timerParagraph = document.getElementById("timerParagraph").innerHTML;
T.min = T.timerParagraph.substring(0, T.timerParagraph.indexOf(":"));
T.time = T.min * 60;
T.min = parseInt(T.min,10)-1;
},
tickClock: function(){
T.sec = T.sec - 1;
if (T.sec < 0) {
T.min -= 1;
T.sec = 59;
}
if (T.min < 0 && T.sec < 0) {
clearInterval(T.interval);
}
if (T.min.toString().length == 1 && T.sec.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = "0" + T.min + ":" + T.sec;
} else if (T.sec.toString().length == 1 && T.min.toString().length == 2) {
document.getElementById("timerParagraph").innerHTML = T.min + ":" + "0" + T.sec;
} else if (T.min.toString().length == 1 && T.sec.toString().length ==1) {
document.getElementById("timerParagraph").innerHTML = "0" + T.min + ":" + "0" + T.sec;
} else {
document.getElementById("timerParagraph").innerHTML = T.min + ":" + T.sec;
}
}
/*
Use T.reset() to set the clock back to the beginning. Use T.start to begin the interval and T.pause to stop the interval.
You may need to record how far within a second the pause is used so you can offset the second when you start again.
This would mean having a settimeout which counts in milliseconds or something.
*/
}
I haven't tested this code so beware of potential typos, although I did double check for those.
To make the timer pause use onclick="T.pause()" within the elements you want to have that functionality.
Or within javascript using
element.onclick = function(){
T.pause();
}
http://www.w3schools.com/jsref/event_onclick.asp

Button Click shouldn't stack the function it triggers but it does

I'm making an timer in Javascript which already works well the only problem is when I keep hitting the
"Start" button it keeps stacking the count function and will count at an higher and higher speed. Tried multiple things to stop it but can't manage myself.
JS:
var startknop = document.getElementById("start");
var stopknop = document.getElementById("stop");
var hour = 0;
var min = 0;
var sec = 0;
startknop.onclick = function()
{
intervalId = setInterval(count, 1000);
}
stopknop.onclick = function()
{
clearInterval(intervalId);
}
counter.innerHTML = hour + "0:" + min + "0:" + sec + "0";
function count() {
sec++;
if(sec <= 9)
{
var seco = "0" + sec;
}
else
{
seco = sec;
}
if(min <= 9)
{
var mino = "0" + min;
}
else
{
mino = sec;
}
if(hour <= 9)
{
var houro = "0" + hour;
}
else
{
houro = hour;
}
if(sec == 60){
sec = 0;
min += 1;
}
if(min == 60){
min = 0;
hour += 1;
}
counter.innerHTML = houro + ":" + mino + ":" + seco;
}
var running = false;
startknop.onclick = function()
{
if(!running)
{
running=true;
intervalId = setInterval(count, 1000);
}
}
when timer is finished set running false.
Wrap the setInterval in an if block with a boolean to check if initialised. You are starting multiple timers, so stop only stops the last one assigned to your global variable. You could also set intervalId to -1 and do a check on it being >0, setting it back to -1 after the clearInterval call.

How to run a timer when a mobile is locked?

I need help for JavaScript . I am using this code to get the execution time of a program:
if(seconds == sec){alert(sec);}
function countdown(element) {
interval = setInterval(function() {
var el = document.getElementById(element);
if(seconds == 0) {
//alert(seconds);
if(minutes == 0) {
el.innerHTML = "00 : 00";
alert("Sorry, you have exceeded our booking time and your order has been cancelled. We are afraid you have to start over!",null,'');
clearInterval(interval);
ajax_load_image();
window.location.href='movies.html';
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes+(minutes > 1 ? ' ' : ' ');
} else {
var minute_text = '0';
}
if(minute_text<0)
{
$('#countdown').css('color','red');
}
var second_text =seconds > 1 ? '' : '';
if(minute_text=='')
{
el.innerHTML ='0 : '+seconds;
}
else
{
if(seconds<10)
{
seconds='0'+seconds;
}
el.innerHTML = '0'+minute_text + ' : ' + seconds + ' ' + second_text + ' Min';
}
seconds--;
}, 1000);
}
When I lock the mobile, the timer automatically stops. What can I do to run the timer when I lock the mobile. Help me.
If you care about the actual amount of time that's passed, don't rely on setInterval() - compare a start time to the current time instead.
var startTime = new Date();
setInterval(function() {
// you might need to call .getTime() here - this works in Chrome,
// but that might be implementation-dependent
var seconds = ((new Date()) - startTime) / 1000;
// etc
});
I don't know how this will work in a mobile environment, but it should timeout properly when you unlock and setInterval() runs again.

Fix timer starting on page load

A simple Javascript timer that runs for 30 seconds and displays a message once it reaches 0. I cant figure out how to get it to start when a button is pressed instead of on pageload. Any help is appreciated :)
<script type="text/javascript">
var sec = 30; // set the seconds
var min = 00; // set the minutes
function countDown() {
sec--;
if (sec == -01) {
sec = 59;
min = min - 1;
} else {
min = min;
}
if (sec<=9) { sec = "0" + sec; }
time = (min<=9 ? "0" + min : min) + " min and " + sec + " sec ";
if (document.getElementById) { theTime.innerHTML = time; }
SD=window.setTimeout("countDown();", 1000);
if (min == '00' && sec == '00') { sec = "00"; window.clearTimeout(SD); alert("Too slow."); }
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(function() {
countDown();
});
</script>
<button onclick='countDown();'>Click Me</button>
And just don't call addLoadEvent() at all.
Add event listener to the button in the load function and then call countDown
addLoadEvent(function() {
var el = document.getElementById("startButton");
el.addEventListener("click", countDown, false);
});
Here the full example link
Here's a full sample with start/stop and remaining time display.
var gTimer = null
function endOfTimer() {
console.log("here");
window.alert('Too slow');
}
function start() {
gTimer = window.setTimeout('endOfTimer', 1 * 1000);
}

Broken closure - please help me fix it

in a related question I have posted this code. It almost works, but the counter doesn't.
Can we fix it? (no jQuery, please)
<script type="text/javascript">
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","minutes":1,"seconds":5},
"trousers":{"id":"trouser1","minutes":10,"seconds":0}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
var minutes = element.minutes;
var seconds = element.seconds;
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
</script>
shoes: <span id="shoe1"></span><br />
trousers: <span id="trouser1"></span><br />
You just need to take the minutes and seconds variable declarations out of the inner function, like this:
function countdown(element) {
var minutes = element.minutes;
var seconds = element.seconds;
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
When you call countdown, you want to fetch the initial values for each countdown and then slowly decrease them (closure makes sure that the values will stay available as long as the anonymous function requires them). What you were doing before was reset the countdown values at the start of each tick, so the countdown never had a chance to... well, count down.
Update:
If you need to update the values inside window.counters while the countdowns are active (although I don't see why you would want to do that; if you want to do anything meaningful with the "current" countdown values, just do it inside the anonymous function), you can simply add this at the end:
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
// ADD THIS:
element.minutes = minutes;
element.seconds = seconds;
Please check this
<html>
<body>
<script type="text/javascript">
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","minutes":1,"seconds":5},
"trousers":{"id":"trouser1","minutes":10,"seconds":0}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
var minutes = element.minutes;
var seconds = element.seconds;
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
element.seconds = seconds;
element.minutes = minutes;
}, 1000);
}
</script>
shoes: <span id="shoe1"></span><br />
trousers: <span id="trouser1"></span><br />
</body>
</html>
Working solution is here.
EDIT: There was a minor issue with the script. I've fixed it.
After recalculating the seconds and minutes you have to set the new values back in the element object.
You're decrementing the wrong variable inside your interval callback. Instead of doing seconds-- and minutes-- you need to reference the element members.
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(element.seconds == 0) {
if(element.minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
element.minutes--;
element.seconds = 60;
}
}
if(element.minutes > 0) {
var minute_text = element.minutes + (element.minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = element.seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + element.seconds + ' ' + second_text + ' remaining';
element.seconds--;
}, 1000);
All of the answers you've been given so far fail to handle one simple fact: setInterval does not happen reliably at the time you set. It can be delayed or even skipped if the browser is busy doing something else. This means you can't rely on your update function to decrement the number of seconds left, you'll start drifting from reality very quickly. Maybe that's okay, maybe not. There's no need for it, in any case: Just calculate when the timeout occurs (e.g., a minute and five seconds from now for your "shoes" counter), and then at each update, calculate how long you have left. That way if an interval got dropped or something, you won't drift; you're deferring the computer's clock.
Here's a procedural version:
// Note that to prevent globals, everything is enclosed in
// a function. In this case, we're using window.onload, but
// you don't have to wait that long (window.onload happens
// *very* late, after all images are loaded).
window.onload = function() {
// Our counters
var counters = {
"shoes":{
"id": "shoe1",
"minutes": 1,
"seconds":5
},
"trousers":{
"id": "trouser1",
"minutes": 10,
"seconds":0
}
};
// Start them
var name;
for (name in counters) {
start(counters[name]);
}
// A function for starting a counter
function start(counter) {
// Find the time (in ms since The Epoch) at which
// this item expires
counter.timeout = new Date().getTime() +
(((counter.minutes * 60) + counter.seconds) * 1000);
// Get this counter's target element
counter.element = document.getElementById(counter.id);
if (counter.element) {
// Do the first update
tick(counter);
// Schedule the remaining ones to happen *roughly*
// every quarter second. (Once a second will look
// rough).
counter.timer = setInterval(function() {
tick(counter);
}, 250);
}
}
// Function to stop a counter
function stop(counter) {
if (counter.timer) {
clearInterval(counter.timer);
delete counter.timer;
}
delete counter.element;
}
// The function called on each "tick"
function tick(counter) {
var remaining, str;
// How many seconds left?
remaining = Math.floor(
(counter.timeout - new Date().getTime()) / 1000
);
// Same as last time?
if (remaining != counter.lastRemaining) {
// No, do an update
counter.lastRemaining = remaining;
if (remaining <= 0) {
// Done! Stop the counter.
str = "done";
alert("Stopped " + counter.id);
stop(counter);
}
else {
// More than a minute left?
if (remaining >= 120) {
// Yup, output a number
str = Math.floor(remaining / 60) + " minutes";
}
else if (remaining >= 60) {
// Just one minute left
str = "one minute";
}
else {
// Down to seconds!
str = "";
}
// Truncate the minutes, just leave seconds (0..59)
remaining %= 60;
// Any seconds?
if (remaining > 0) {
// Yes, if there were minutes add an "and"
if (str.length > 0) {
str += " and ";
}
// If only one second left, use a word; else,
// a number
if (remaining === 1) {
str += "one second";
}
else {
str += Math.floor(remaining) + " seconds";
}
}
// Finish up
str += " left";
}
// Write to the element
counter.element.innerHTML = str;
}
}
};​
Live example
Here's an OOP version (using the module pattern so Counter can have named functions and a private one [tick]):
// A Counter constructor function
var Counter = (function() {
var p;
// The actual constructor (our return value)
function Counter(id, minutes, seconds) {
this.id = id;
this.minutes = minutes || 0;
this.seconds = seconds || 0;
}
// Shortcut to the prototype
p = Counter.prototype;
// Start a counter
p.start = Counter_start;
function Counter_start() {
var me = this;
// Find the time (in ms since The Epoch) at which
// this item expires
this.timeout = new Date().getTime() +
(((this.minutes * 60) + this.seconds) * 1000);
// Get this counter's target element
this.element = document.getElementById(this.id);
if (this.element) {
// Do the first update
tick(this);
// Schedule the remaining ones to happen *roughly*
// every quarter second. (Once a second will look
// rough).
this.timer = setInterval(function() {
tick(me);
}, 250);
}
}
// Stop a counter
p.stop = Counter_stop;
function Counter_stop() {
if (this.timer) {
clearInterval(this.timer);
delete this.timer;
}
delete this.element;
}
// The function we use to update a counter; not exported
// on the Counter prototype because we only need one for
// all counters.
function tick(counter) {
var remaining, str;
// How many seconds left?
remaining = Math.floor(
(counter.timeout - new Date().getTime()) / 1000
);
// Same as last time?
if (remaining != counter.lastRemaining) {
// No, do an update
counter.lastRemaining = remaining;
if (remaining <= 0) {
// Done! Stop the counter.
str = "done";
alert("Stopped " + counter.id);
stop(counter);
}
else {
// More than a minute left?
if (remaining >= 120) {
// Yup, output a number
str = Math.floor(remaining / 60) + " minutes";
}
else if (remaining >= 60) {
// Just one minute left
str = "one minute";
}
else {
// Down to seconds!
str = "";
}
// Truncate the minutes, just leave seconds (0..59)
remaining %= 60;
// Any seconds?
if (remaining > 0) {
// Yes, if there were minutes add an "and"
if (str.length > 0) {
str += " and ";
}
// If only one second left, use a word; else,
// a number
if (remaining === 1) {
str += "one second";
}
else {
str += Math.floor(remaining) + " seconds";
}
}
// Finish up
str += " left";
}
// Write to the element
counter.element.innerHTML = str;
}
}
// Return the constructor function reference. This
// gets assigned to the external var, which is how
// everyone calls it.
return Counter;
})();
// Note that to prevent globals, everything is enclosed in
// a function. In this case, we're using window.onload, but
// you don't have to wait that long (window.onload happens
// *very* late, after all images are loaded).
window.onload = function() {
// Our counters
var counters = {
"shoes": new Counter("shoe1", 1, 5),
"trousers": new Counter("trouser1", 10, 0)
};
// Start them
var name;
for (name in counters) {
counters[name].start();
}
};​
Live example
More about closures here.
I think it'd make your code a lot cleaner and save you some ifs if you would keep the timeout in code as seconds, rather than minutes and seconds:
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","seconds":65},
"trousers":{"id":"trouser1","seconds":600}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(element.seconds == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
}
var minutes = (element.seconds - (element.seconds % 60)) / 60;
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = (element.seconds%60) > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + (element.seconds%60) + ' ' + second_text + ' remaining';
element.seconds--;
}, 1000);
}​
(I'd post as a comment if it weren't for all the code...)

Categories

Resources