I am trying to build multiple timers for my web page, so far I have,
$('.timer').each(function() {
var timer = setInterval(function() {
var time = $(this).text().split(':');
var minutes = parseInt(time[0], 10);
var seconds = parseInt(time[1], 10);
// TIMER RUN OUT
if (!minutes && !seconds) {
// CLEAR TIMER
clearInterval(timer);
// MINUS SECONDS
} else {
seconds -= 1;
}
// MINUS MINUTES
if (seconds < 0 && minutes != 0) {
minutes -= 1;
seconds = 59;
// ADD ZERO IF SECONDS LESS THAN 10
} else {
if (seconds < 10) {
seconds = '0' + seconds;
}
}
// ADD ZERO IF MINUTES LESS THAN 10
if (minutes < 10) {
minutes = '0' + minutes;
}
}, 1000);
});
This doesn't work though! Where am I going wrong!
Thanks
First, inside your setInterval callback, this no longer refers to the .timer element. Try changing that to self and add var self = this; before the call to setInterval. Second, you never write your time back to your .timer element.
Are you trying to display a clock counting down? In that case, you're not updating the text after all the calculations. Try adding:
$(this).text(minutes+':'+seconds);
Related
// This timer keeps reseting back to 2:00 after it reaches 1 minute. Also i do not get a notification that says times up at the right time. Can someone please correct the code. Also the stop/resume timer button also has to stay functional.
var isRunning = false;
var ticker; //this will hold our setTimeout
var seconds,
minutes;
function countdown(mins, secs) {
//i made these global, so we can restart the timer later
seconds = secs || 60; //if user put in a number of minutes, use that. Otherwise, use 60
minutes = mins;
console.log('time stuff',mins,secs,minutes,seconds)
function tick() {
var counter = document.getElementById("timer");
var current_minutes = mins - 1
seconds--;
counter.innerHTML =
current_minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
if (seconds < 1 && minutes) {
//seconds reached 0, and still minutes left;
seconds=60;
minutes--;
}
if ((seconds > 0 || minutes > 0) && isRunning) {
ticker = setTimeout(tick, 1000);
} else if(isRunning){
console.log(seconds,minutes,isRunning)
alert('Time\'s up, brah!')
}
}
tick();
}
function timeToggle() {
isRunning = !isRunning; //if it's false, set it true. If it's true, set it false.
if (!isRunning) {
clearTimeout(ticker); //or whatever else you set the initial timeOut to.
} else {
//not running! and time is defined;
var sec = seconds||60;
console.log('def!',minutes, sec)
countdown(minutes, sec);
}
}
isRunning = true;
countdown(2);
<div id="timer">2:00</div>
<button onclick="timeToggle()">Stop time</button>
There is a small flaw in your logic.
During the countdown initialization your doing
seconds = secs || 60;
Which effectively add 60 seconds to the time you want if you don't initialize the seconds. see:
function countdownInit(mins, secs) {
seconds = secs || 60;
minutes = mins;
console.log(mins + 'min ' + seconds + 'sec');
}
countdownInit(1, 30) // ok
// 1min 30sec
countdownInit(1) // not ok
// 1min 60sec
// thats 2 minutes
The second issue here is that you use a var current_minutes that equals minutes - 1 to display the time. So you are not showing the real counter.
the fix is as follow:
function countdown(mins, secs) {
seconds = secs;
minutes = mins;
// if secs is 0 or uninitialized we set seconds to 60 and decrement the minutes
if(!secs) {
minutes--;
seconds = 60;
}
function tick() {
var counter = document.getElementById("timer");
seconds--;
// we use minutes instead of current_minutes in order to show what's really in our variables
counter.innerHTML =
minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
// rest of code
}
// rest of code
}
I tried to keep as much as your code as possible.
I am working on a timer that runs for a set amount of minutes, then starts over for a break period that counts down and then goes back to the original amount of minutes. I'm struggling with the logic. So far, I have it running down the original time, then running down the break timer but I need help making it return to the original time and loop this infinitely (or until the stop button is pressed). Here's what I have:
function timer(minutes, breakLength) {
--minutes;
timerId = setInterval(function() {
if (minutes >= 0) {
if (seconds > 0) {
--seconds;
}
if (seconds == 0 && minutes == 0) {
playSound();
isBreak = true;
minutes = breakLength;
$('.type').html('Break');
$('.timer').html(minutes + ':00');
};
if (seconds === 0) {
seconds = 59;
--minutes;
}
if (seconds < 10) {
seconds = '0' + seconds;
}
$('.timer').html(minutes + ':' + seconds);
}
}, 1000);
}
How can I make this repeat itself?
Define a new variable as a timeout id holder (let's call it resetTimeout) in your timer function scope:
var resetTimeout = null;
Add this additional code to the main function
var runFor = 60000; // 60000ms or 1 minute
Add logic in the main interval (first line):
if(runFor <= 0) {
if(!resetTimeout) {
// Create a reset timeout
resetTimeout = setTimeout(function(){ runFor = 60000; resetTimeout = null; }, breakLength);
}
return;
}
else {
runFor -= 1000; // Deduct time of this interval
}
This logic deducts 1000 ms or 1 second from runFor until it is fully consumed. Then creates a timeOut function that will reset it back to its original value and returns the current function until runFor is renewed. I used 60000 ms as an example and you can see the correct version in the full code below. Why do we assign the timeout to a variable? It is simple, we don't want to create more than one timeout. We'll set the timeout to null to allow recreation on the next interval.
Note that there are better ways of doing this but I decided to make as little modifications to your code as possible.
Here is the working code:
function timer(minutes, breakLength) {
var seconds = 0;
var originalMinutes = minutes;
var resetTimeout = null;
var totalRunFor = minutes * 60 * 1000; // Since minutes are independent
var runFor = totalRunFor;
timerId = setInterval(function() {
if(runFor <= 0) {
if(!resetTimeout) {
// Create a reset timeout
resetTimeout = setTimeout(function(){ runFor = totalRunFor; resetTimeout = null; }, breakLength);
}
return;
}
else {
runFor -= 1000; // Deduct time of this interval
}
if (minutes >= 0) {
if (seconds > 0) {
--seconds;
}
if (seconds == 0 && minutes == 0) {
//playSound();
isBreak = true;
minutes = originalMinutes;
$('.type').html('Break');
$('.timer').html(minutes + ':00');
};
if (seconds === 0) {
seconds = 59;
--minutes;
}
$('.timer').html(minutes + ':' + ((seconds < 10)?'0':'') + seconds);
}
}, 1000);
}
timer(1, 10000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Run for a minute and stop for 10 seconds indefinitely.
<div class='type'></div>
<div class='timer'></div>
It may be conceptually easier to seperate this into multiple functions within the timer function
function timer(minutes, breakLength) {
var ci = null;
function ACountdown(minutes, callback) {
var mm = minutes,
ss = 0;
ci = setInterval(function () {
--ss;
if (ss < 0)
ss += 60, --mm;
if (mm < 0) {
// done
clearInterval(ci);
setTimeout(callback, 0);
} else {
$('.timer').html(mm + ':' + seconds);
}
}, 1e3);
}
function A() {
// returned from break
$('.type').html = 'Countdown';
ACountdown(minutes, B);
}
function B() {
// just finished countdown
playSound();
// going on break
$('.type').html = 'Break';
ACountdown(breakLength, A);
}
// set up any click handlers here, e.g.
document.getElementById('cancel_button').addEventListener('click', function c() {
this.removeEventListener('click', c);
clearInterval(ci);
});
// start invocation chain
A();
}
I am trying to make a timer using JavaScript. The problem is, I can't get the timer to stop when it reaches 0. I have tried using return and if statements, but nothing seems to be working. Am I on the right track, or is there a better way to do this?
<input type="text" id="minutes" style="width:30px;text-align:center" maxlength="2" placeholder="00">
<span>:</span>
<input type="text" id="seconds" style="width:30px;text-align:center" maxlength="2" placeholder="00">
<button onclick="timer()">Set</button>
<script>
//This prototype correctly uses the modulus function when dealing with negative numbers.
Number.prototype.mod = function (m) {
return ((this % m) + m) % m
}
function timer() {
var minutes = document.getElementById('minutes').value //Value of minutes box.
var seconds = document.getElementById('seconds').value //Value of seconds box.
var initial = seconds * 1000 //Amount of seconds (converted to milliseconds) initially set.
var milliseconds = (minutes * 60000) + (seconds * 1000) //Total amount of milliseconds set.
setTimeout(function () {
alert("Time's up!")
}, milliseconds) //Display a message when the set time is up.
/*\Decreases the minute by one after the initially set amount of seconds have passed.
|*|Then, clears the interval and sets a new one to decrease the minute every 60 seconds.
\*/
test = setInterval(function () {
minutes--;
document.getElementById('minutes').value = minutes;
clearInterval(test)
setInterval(function () {
minutes--;
document.getElementById('minutes').value = minutes
}, 60000)
}, initial)
//Seconds are set to decrease by one every 1000 milliseconds, then be displayed in the seconds box.
setInterval(function () {
seconds--;
document.getElementById('seconds').value = seconds.mod(60)
}, 1000)
}
You have four different timer functions (setTimer and two setIntervals) when you only need one. JSFiddle
function timer() {
var minutes = document.getElementById('minutes').value //Value of minutes box.
var seconds = document.getElementById('seconds').value //Value of seconds box.
var intervalTimer = setInterval(function () {
seconds--;
if (seconds < 0) {
seconds = 59;
minutes--;
}
document.getElementById('minutes').value = minutes;
document.getElementById('seconds').value = seconds;
if (minutes == 0 && seconds == 0) {
alert("Time's up!");
clearInterval(intervalTimer);
}
}, 1000);
}
In general, you need to make sure every setInterval is given a name (var x = setInterval...) and cleared later on (clearInterval(x)). You could have separate timers (one for the minutes which starts after the given number of seconds then repeats every 60 seconds, one for seconds, and one to display the message) if you really want to for some reason, as long as you clear all of the interval timers once the countdown reaches zero.
Using two timers might make sense, however. This would make sure that the Time's up message really appears when it's supposed to, even if there is any imprecision in the interval timer.
function timer() {
var minutes = document.getElementById('minutes').value,
seconds = document.getElementById('seconds').value,
intervalTimer = setInterval(function () {
seconds--;
if (seconds < 0) {
seconds = 59;
minutes--;
}
document.getElementById('minutes').value = minutes;
document.getElementById('seconds').value = seconds;
}, 1000);
setTimer(function () {
alert("Time's up!");
clearInterval(intervalTimer);
}, minutes * 60000 + seconds * 1000);
}
I made improvements to Stuart's answer: fiddle
Basically the same thing, except it works properly:
function clearTimer() {
clearInterval(intervalTimer);
}
var intervalTimer;
function timer() {
var minutes = document.getElementById('minutes').value //Value of minutes box.
var seconds = document.getElementById('seconds').value //Value of seconds box.
intervalTimer = setInterval(function () {
seconds--;
if (seconds < 0) {
seconds += 60;
minutes--;
}
if (minutes < 0) {
alert("Time's up!");
clearTimer();
} else {
document.getElementById('minutes').value = minutes;
document.getElementById('seconds').value = seconds;
}
}, 1000);
}
Im not a great javascript guy, but maybe this will help. i made this in typescript http://www.typescriptlang.org/Playground/
but i would do timing different and use the javascript date object and calculate differences. This is a simple example of how i would start to create a time (without the date object)
javascript
var Timer = (function () {
function Timer(time) {
this.accuracy = 1000;
this.time = time;
}
Timer.prototype.run = function (button) {
var _this = this;
this.time -= 1; //this is inaccurate, for accurate time use the date objects and calculate the difference.
//http://www.w3schools.com/js/js_obj_date.asp
button.textContent = this.time.toString();
if (this.time > 0) {
setTimeout(function () {
return _this.run(button);
}, 1000);
}
};
return Timer;
})();
var time = new Timer(10);
var button = document.createElement('button');
time.run(button);
document.body.appendChild(button);
typescript(in case you wonder)
class Timer {
accuracy = 1000;//update every second
time: number;
constructor(time: number) {
this.time = time;
}
run(button: HTMLButtonElement) {
this.time -=1;//this is inaccurate, for accurate time use the date objects and calculate the difference.
//http://www.w3schools.com/js/js_obj_date.asp
button.textContent = this.time.toString();
if(this.time > 0)
{
setTimeout(()=> this.run(button),1000);
}
}
}
var time = new Timer(10)
var button = document.createElement('button');
time.run(button);
document.body.appendChild(button);
function countdown(element, minutes, seconds) {
// set time for the particular countdown
var time = minutes*60 + seconds;
var interval = setInterval(function() {
var el = document.getElementById(element);
// if the time is 0 then end the counter
if(time == 0) {
//el.innerHTML = "countdown's over!";
// document.getElementById("timer").style.visibility="hidden";
clearInterval(interval);
return;
}
var hour=Math.floor( time / (60*60) );
if (hour < 10) hour = "0" + hour;
var minutes = 0;
if(time>=60 && hour>0)
minutes=Math.floor( (time / 60 )-60);
else{
minutes=Math.floor( (time / 60 ));
}
if (minutes < 10) minutes = "0" + minutes;
var seconds = time % 60;
if (seconds < 10) seconds = "0" + seconds;
// var text = hour+":"+minutes; //+ ':' + seconds;
var text = minutes; //+ ':' + seconds;
el.innerHTML = text;
time--;
}, 1000);
}
when i am calling the method 2wice, its creating flickering effect.
i.e. countdown(element, 50, 0);
it count downs but if i call it again i.e. countdown(element, 35, 0); it is flicks showing both the countddowns
You need to cancel the interval when the plugin initializes if you are going to call it on the same element. Otherwise you are running two intervals at once.
I'd suggest returning the interval from your function and allowing yourself to pass that interval in as a parameter. That way you can always cancel the interval before starting it (in case there is already one going). If you pass null for the interval var, you can still run clearInterval() without throwing errors.
For example:
function countdown(element, minutes, seconds, interval) {
// set time for the particular countdown
var time = minutes*60 + seconds;
clearInterval(interval);
return setInterval(function() {
...
Your first call could be:
var savedInterval = countdown('some-ele-id', 1, 1, null);
And your second call could be:
var interval = countdown('some-ele-id', 1, 1, savedInterval);
Another solution would be to save the interval as a global variable and cancel it without passing it as a parameter inside your plugin.
An alternative to avoid modify your code is:
var interval;
...
function countdown(element, minutes, seconds) {
// set time for the particular countdown
var time = minutes*60 + seconds;
interval = setInterval(function() {
...
And in your second call:
clearInterval(interval);
countdown(element, 35, 0);
Here's my html:
<div class="timer">Not Started</div>
And JS/JQ:
var seconds = 10;
var minutes = 0;
setTimeout("updateTimer()", 1000);
function updateTimer() {
if (seconds == 0 && minutes != 0) {
minutes -= minutes;
seconds = 59;
alert (seconds);
} else if (seconds == 1 && minutes == 0) {
alert ('done');
} else {
seconds = seconds - 1;
//alert (seconds);
$(".timer").replaceWith(seconds);
}
setTimeout("updateTimer()", 1000);
}
Instead of replacing Not Started with 10, 9, 8..., Not Started disappears.
$(".timer").text(seconds);
You can't replace a DOM node with a string.
See an example.
You could simplify your logic further by making use of setInterval instead of setTimeout, and use total seconds for easier calculations and remove minutes.
var seconds = 10, minutes = 0;
var totalSeconds = (minutes * 60) + seconds;
var timerId = setInterval(updateTimer, 1000);
function updateTimer() {
$('.timer').text(totalSeconds % 60);
if (totalSeconds == 0) {
alert("done");
clearInterval(timerId);
}
totalSeconds--;
}
replaceWith will replace the entire div, not just the contents. Try this instead:
$(".timer").html(seconds);