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();
}
Related
There is a counter that counts down from 25. When I click on the link of the page, it continues to count from wherever it is left. So when I start at 25 and exit the page, it shows 15 when I come back 10 seconds later. But when I open it with a different browser, there is a different number, so the counter does not always show the same value. What could be the reason for this and what can I do?
var interval = 25000;
function reset() {
localStorage.endTime = +new Date() + interval;
}
if (!localStorage.endTime) {
reset();
}
function millisToMinutesAndSeconds(millis) {
var seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
setInterval(function () {
var remaining = localStorage.endTime - new Date();
if (remaining >= 0) {
document.getElementById("timer").innerText =
millisToMinutesAndSeconds(remaining);
} else {
reset();
}
}, 100);
I have a html page where there is counter starts on page loading. But the problem is if someone refresh or reloads the page the counter restarts. I dont know how to use local storage or cookies to make sure my counter does not reset upon reload. I am aware of the similar questions available here but my issue is i want local storage to be part of a function (countDown()).
Here is the code I tried:
<script>
var timer;
function countDown(i, callback) {
//callback = callback || function(){};
timer = setInterval(function() {
minutes = parseInt(i / 60, 10);
seconds = parseInt(i % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
document.getElementById("displayDiv ").innerHTML = "Time (h:min:sec) left for this station is " + "0:" + minutes + ":" + seconds;
i-- || (clearInterval(timer), callback());
}, 1000);
}
window.onload = function() {
countDown(60, function() {
$('#myModal').modal('show');
});
};
</script>
First, persist your current counter value to the session storage (with a specific key) at each iteration. You may only persist/update the value when the counter is greater than 0, and then clear the storage key once counter reached 0.
const COUNTER_KEY = 'my-counter';
function countDown(i, callback) {
//callback = callback || function(){};
timer = setInterval(function() {
minutes = parseInt(i / 60, 10);
seconds = parseInt(i % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
document.getElementById("displayDiv").innerHTML = "Time (h:min:sec) left for this station is " + "0:" + minutes + ":" + seconds;
if ((i--) > 0) {
window.sessionStorage.setItem(COUNTER_KEY, i);
} else {
window.sessionStorage.removeItem(COUNTER_KEY);
clearInterval(timer);
callback();
}
}, 1000);
}
Then on the window.onload function, first check if there is a value under the above key on the session storage. If a value is there, start the countdown from that value. Otherwise, start from your default value (60).
window.onload = function() {
var countDownTime = window.sessionStorage.getItem(COUNTER_KEY) || 60;
countDown(countDownTime, function() {
$('#myModal').modal('show');
});
};
You can try using localStorage as follows:
var timer;
function countDown(i, callback) {
//callback = callback || function(){};
timer = setInterval(function () {
minutes = parseInt(i / 60, 10);
seconds = parseInt(i % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
document.getElementById("displayDiv").innerHTML = "Time (h:min:sec) left for this station is " + "0:" + minutes + ":" + seconds;
// update the persisted time interval
localStorage.setItem('timeLeft', i);
i-- || (clearInterval(timer), callback());
}, 1000);
}
window.onload = function () {
let timeInterval = 100;
//check if you have the last counter value
let timeLeft = localStorage.getItem('timeLeft');
if (isNaN(timeLeft)) {
//save the current interval
localStorage.setItem('timeLeft', timeInterval);
} else if (timeLeft == 0) {
//save the current interval
localStorage.setItem('timeLeft', timeInterval);
} else {
// take the last saved value
timeInterval = timeLeft;
}
countDown(timeInterval, function () {
$('#myModal').modal('show');
});
};
I think what you want is a property called sessionStorage which stores information in the browser permanently.
here's a link explaining it
https://www.w3schools.com/jsref/prop_win_sessionstorage.asp
I have a made a promise based function for this.
It uses localStorage to store the countdown every 1 second and, once the time is over, it returns you a promise.
I use it with React and React Router on production, it's been working great.
function countdown(interval = 5000) {
return new Promise(async (resolve, reject) => {
let intervalLoop = null
function countTimer() {
if (!localStorage.endTime) {
localStorage.endTime = +new Date() + interval
}
let remaining = localStorage.endTime - new Date()
if (remaining >= 0) {
let currentTime = Math.floor(remaining / 1000)
console.log("Countdown current time:", currentTime)
} else {
clearInterval(intervalLoop)
resolve(true)
}
}
intervalLoop = setInterval(countTimer, 1000)
})
}
Make sure you are inside an async function to use it:
(async _ => {
console.log("Countdown starts now...")
await countdown(5000)
console.log("Countdown is over!");
})()
This question already has answers here:
Stop setInterval
(6 answers)
Closed 4 years ago.
I am creating a simple JavaScript Timer. Firstly, here's my code that I'm working with:
//Variables used for the timer
var timer = "waiting",
seconds = 0,
minutes = 0,
extraFill1 = "",
extraFill2 = "";
//Check whether to start or stop the timer
function toggleClock() {
if (timer === "waiting") {
setInterval(start, 1000);
timer = "executing";
document.getElementById("btn").innerText = "Stop";
}
else {
clearInterval(start);
timer = "waiting";
document.getElementById("btn").innerText = "Start";
}
}
//Increment seconds and display correctly
function start() {
seconds++;
//Add a leading zero for 1 digit numbers (seconds)
if (seconds < 10 || seconds === 60) {
extraFill1 = "0";
}
else {
extraFill1 = "";
}
//Increment minute when seconds reaches 60
if (seconds === 60) {
minutes += 1;
seconds = 0;
}
//Add a leading zero for 1 digit numbers (minutes)
if (minutes < 10) {
extraFill2 = "0";
}
else {
extraFill2 = "";
}
//display results
document.getElementById("result").innerHTML = extraFill2 + minutes + ":" + extraFill1 + seconds;
}
In my first function toggleClock(), I have stated that if timer !== waiting, then we do clearInterval(start). I would like to clearInterval(start) if timer === "executing. This does not work (which I think is because it has a local scope?). To solve this, I attempted writing:
var myTimer = setInterval(start, 1000);
I planned on calling myTimer when I wanted to start the timer. Instead, the interval kicks off as soon as the page is loaded (and the variable is declared).
How might I set an interval of a function (on button click) with the ability to stop/clear the interval (by pressing the same button) later?
You need to pass to clearInterval the result of your call to setInterval earlier; you don't pass a function to clearInterval, you pass it the intervalID. For example:
let clockInterval;
function toggleClock() {
if (timer === "waiting") {
clockInterval = setInterval(start, 1000);
timer = "executing";
document.getElementById("btn").innerText = "Stop";
}
else {
clearInterval(clockInterval);
timer = "waiting";
document.getElementById("btn").innerText = "Start";
}
}
I'm creating a countdown timer based on a users input. When the user pauses and resumes, the timer restarts at the initial inputed value and fails to resume from the current interval. I've uploaded the code into Codepen.
http://codepen.io/alivera/pen/JGpvRx
//Timer
var myTimer;
var duration = sessionCounter * 60;
var startTimer = function() {
minutes = parseInt(duration / 60);
seconds = parseInt(duration % 60);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
document.getElementById("clockTimer").innerHTML = minutes + ":" + seconds;
if (--duration < 0) {
document.getElementById("toggleStatus").innerHTML = "<br>Break!";
}
};
//Start Timer
var go = function() {
myTimer = setInterval(startTimer, 1000);
document.getElementById('start').innerHTML = "Stop";
document.getElementById('start').className = "btn btn-danger";
document.getElementById("start").onclick = stop;
};
//Stop Timer
var stop = function() {
clearInterval(myTimer);
document.getElementById('start').innerHTML = "Start";
document.getElementById('start').className = "btn btn-success";
document.getElementById("start").onclick = go;
};
duration.onload = stop();
You're loading the time to count down from from the #clockTimer element:
var sessionCounter = document.getElementById("clockTimer").innerHTML;
This is bad because the contents of that element are changing. Often.
And parseInt on the next line only is only giving you the number before the colon. Your best bet for solving this problem would be storing the current time remaining and the previously set time in separate variables, as I have done below.
Your code was a little difficult to work with, so while correcting the error(s), I ended up almost completely rewriting it.
Here's my version; I'll explain it line-by-line (or section-by-section, or whatever):
First, put all of our elements in to easy-to-use (and type) variables:
var subBreakButton = document.getElementById("subBreakButton"),
breakTimer = document.getElementById("breakTimer"),
addBreakButton = document.getElementById("addBreakButton"),
subSessionButton = document.getElementById("subSessionButton"),
sessionTimer = document.getElementById("sessionTimer"),
addSessionButton = document.getElementById("addSessionButton"),
breakSession = document.getElementById("breakSession"),
clockTimer = document.getElementById("clockTimer"),
These variables are in seconds (thus m * s):
breakLength = 5 * 60, // Minutes to seconds
sessionLength = 25 * 60, // Minutes to seconds
sessionTimeLeft = sessionLength;
Next, a helper method that formats times into a mm:ss ... format:
function timeString (seconds) {
var minutes = parseInt(seconds / 60) + "",
seconds = parseInt(seconds % 60) + "";
if (minutes.length === 1)
minutes = "0" + minutes;
if (seconds.length === 1)
seconds = "0" + seconds;
return minutes + ":" + seconds;
}
Third, event listeners for the plus and minus buttons:
// Event Listeners
addBreakButton.addEventListener("click", function () {
breakLength += 1 * 60;
breakTimer.innerHTML = timeString(breakLength);
});
subBreakButton.addEventListener("click", function () {
breakLength -= 1 * 60;
if (breakLength < 0)
breakLength = 0;
breakTimer.innerHTML = timeString(breakLength);
});
addSessionButton.addEventListener("click", function () {
sessionLength += 1 * 60;
sessionTimer.innerHTML = timeString(sessionLength);
});
subSessionButton.addEventListener("click", function () {
sessionLength -= 1 * 60;
if (sessionLength < 0)
sessionLength = 0;
sessionTimer.innerHTML = timeString(sessionLength);
});
And, the fun part:
// Timer
var myTimer;
function startTimer () {
if (myTimer) // Check to see if a timer was already running, and if so, stop it
stopTimer();
sessionTimeLeft = sessionLength;
myTimer = setInterval(function () {
sessionTimeLeft--;
if (sessionTimeLeft <= 0) {
sessionTimeLeft = 0;
stopTimer();
}
clockTimer.innerHTML = (sessionTimeLeft <= 0? "Break!": timeString(sessionTimeLeft));
}, 1000);
}
function stopTimer () {
clearInterval(myTimer);
myTimer = 0;
}
Lastly, wrappers:
// Start Timer
function go() {
startTimer();
}
//Pause Timer
function stop() {
stopTimer();
}
Codepen: http://codepen.io/anon/pen/ZQjLZE?editors=1010
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);