Trouble with setInterval() and clearInterval() while creating a pomodoro timer - javascript

I've been learning javascript for the last few months, so I am a rookie. Longtime reader, first time poster. The full code for what I'm working on is available here in this codepen
https://codepen.io/dconnenc/pen/yLpwrQO?editors=1011
I'm hitting a pretty bad wall.
My current problem is keeping my clock maintaining proper mm:ss format, and getting it to tick down to 00:00.
min = min < 10 ? "0" + min : min;
sec = sec < 10 ? "0" + sec : sec;
I had been using the above, but it became too fragile inside of the interval function and begin adding 0s at the start at every interval. I have a fix so that it stops adding zeroes, but now it only ticks down to 0:00, and never reaches 00:00.
sessionTime = sessionTime < 10 && !sessionTime.toString().startsWith("0") ? "0" + sessionTime : sessionTime;
sec = sec < 10 && !sessionTime.toString().startsWith("0") ? "0" + sec : sec;
I've got stop for the day, and take a break, but a larger snippet of the code is below, and of course the link to the full codepen is above.
function timer() {
//ticks down timer
if(sec > 0){
sec--;
} else if (sec == 0 && sessionTime == 0) {
console.log("check");
document.getElementById('time-left').textContent = breakTime + ":" + sec;
} else if (sec == 0) {
sessionTime--;
sec = 59;
}
//mm:ss format (started running into an error with the interval returning extra 0s)
sessionTime = sessionTime < 10 && !sessionTime.toString().startsWith("0") ? "0" + sessionTime : sessionTime;
sec = sec < 10 && !sessionTime.toString().startsWith("0") ? "0" + sec : sec;
//updates display
document.getElementById('time-left').textContent = sessionTime + ":" + sec;
}

Try this with the min then with the seconds
if(sessionTime.toString().length === 1){
if(sessionTime === 0){
sessionTime = sessionTime;
}else{
sessionTime = "0"+sessionTime;
}
}

This implements the expected behavior of your timer (Using a 500ms interval and 3 second "minutes") using a somewhat minimized example. Session Time is hardcoded to 3 minutes.
Mixing strings and integers in JavaScript can easily lead to coding issues like the ones you have experienced, especially when trying to use the + operator on mixed strings and integers. Best to avoid that in your control variables and lead with an obvious string when you're setting displays.
let oneSecond = 500;
let oneMinute = 3;
let sessionTime = 3;
var secondsRemaining;
var intervalId;
function reset() {
console.log('check')
clearInterval(intervalId);
intervalId = 0
}
function start() {
secondsRemaining = sessionTime * oneMinute;
setTimeLeft()
intervalId = setInterval(timer, oneSecond)
}
function timer() {
if ( --secondsRemaining <= 0 ) {
reset()
}
setTimeLeft()
}
function setTimeLeft() {
document.getElementById('time-left').textContent = `${(''+ ~~(secondsRemaining / oneMinute)).padStart(2, '0')}:${(''+ secondsRemaining % oneMinute).padStart(2, '0')}`;
}
start()
<span id='time-left'></span>
This version handles multiple timers.
let oneSecond = 500;
let oneMinute = 3;
let sessionTime = 3;
function Timer(seconds, resetCallback, updateCallback, period) {
this.s = seconds;
this.p = period || oneSecond;
this.callback = resetCallback || Function.prototype;
this.update = updateCallback || Function.prototype;
this.reset = function() {
this.intervalId = clearInterval(this.intervalId)
this.callback()
}
this.start = function() { this.update(this.s); this.intervalId = setInterval(this.timer.bind(this), [ this.p ]) }
this.timer = function() { if (--this.s <= 0) this.reset(); this.update(this.s) }
}
function setTimeLeft(sec, id) {
document.getElementById(id || 'time-left').textContent = `${(''+ ~~(sec / oneMinute)).padStart(2, '0')}:${(''+ sec % oneMinute).padStart(2, '0')}`;
}
document.addEventListener("DOMContentLoaded", function() {
new Timer(sessionTime * oneMinute, () => console.log('check 1000ms'), setTimeLeft, 1000).start()
new Timer(6 * oneMinute, () => console.log('check 500ms'), (s) => { setTimeLeft(s, 'time-left2')}, 500).start()
new Timer(15 * oneMinute, () => console.log('check 300ms'), (s) => { setTimeLeft(s, 'time-left3')}, 200).start()
})
<span id='time-left'></span><br />
<span id='time-left2'></span><br />
<span id='time-left3'></span>

Related

Javascript stop countdown

I have a countdown in js and I can't add a trick I would like.
When the counting ends, it does not stop. Negative numbers start and instead I would like it to stop at 0 once the time has expired. How can I?
var counter = null;
window.onload = function() {
initCounter();
};
function initCounter() {
// get count from localStorage, or set to initial value of 1000
count = getLocalStorage('count') || 1000;
counter = setInterval(timer, 1000); //1000 will run it every 1 second
}
function setLocalStorage(key, val) {
if (window.localStorage) {
window.localStorage.setItem(key, val);
}
return val;
}
function getLocalStorage(key) {
return window.localStorage ? window.localStorage.getItem(key) : '';
}
function timer() {
count = setLocalStorage('count', count - 1);
if (count == -1) {
clearInterval(counter);
return;
}
var seconds = count % 60;
var minutes = Math.floor(count / 60);
var hours = Math.floor(minutes / 60);
minutes %= 60;
hours %= 60;
document.getElementById("countdown").innerHTML = hours + " ore " + minutes + " min " + seconds + " sec";
}
The problem is that you were putting count with -1 value in LocalStorage.
count = setLocalStorage('count', count - 1);
And after page reload you kept subtracting 1 from -1 and you got -2, which your condition count == -1 couldn't catch. Solution is to put next count value in LocalStorage after you check if need to continue your timer or not.
<script type="text/javascript">
let count = 0;
let counter = null;
window.onload = function() {
initCounter();
};
function initCounter() {
// get count from localStorage, or set to initial value of 1000
count = Number(getLocalStorage('count')) || 5;
counter = setInterval(timer, 1000); //1000 will run it every 1 second
}
function setLocalStorage(key, val) {
if (window.localStorage) {
window.localStorage.setItem(key, val);
}
return val;
}
function getLocalStorage(key) {
return window.localStorage ? window.localStorage.getItem(key) : '';
}
function timer() {
const nextCount = count - 1
if (nextCount < 0) {
clearInterval(counter);
return;
}
count = setLocalStorage('count', nextCount);
const seconds = count % 60;
let minutes = Math.floor(count / 60);
let hours = Math.floor(minutes / 60);
minutes %= 60;
hours %= 60;
document.getElementById("timer").innerHTML = hours + " ore " + minutes + " min " + seconds + " sec";
}
</script>
<div id="timer"></div>
Hope it helps :)
Change this:
if (count == -1) {
clearInterval(counter);
return;
}
To this:
if (count < 0) {
clearInterval(counter);
localStorage.removeItem('count');
return;
}
Always make your conditions as strict as you can, or you will run into trouble. You don't actually care that it's equal to -1. You care that it's below 0.
In your original code, it stops fine when the page is loaded without localStorage. But at the end, you set the localStorage to -1. When you refresh, you set it to -2 (count - 1) and start the counter going into the negatives. Your condition is never checked against that -1 value which was stored.

Updating the button element as a countdown timer through javascript

I want to create a countdown timer which looks like an fps counter for webpage...
after hours of time spent i m not able to find out what is wrong.....help
<script>
var myvar = setInterval(function () { startTimer() }, 1000);
function startTimer() {
var presentTime = 17 + ":" + 00;
var timeArray = presentTime.split(/[:]+/);
var m = timeArray[0];
var s = checkSecond((timeArray[1] - 1));
if (s == 59) {
m = m - 1
}
//if(m<0){alert('timer completed')}
var button2 = document.createElement("Button2");
var interval = m + s;
button2.innerHTML = Math.round(interval);
button2.style = "top:0; left:0rem; height:10% ;color: black; background-color: #ffffff;position:fixed;padding:20px;font-size:large;font-weight: bold;";
setTimeout(startTimer, 1000);
document.body.appendChild(button2);
}
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {
sec = "0" + sec
}; // add zero in front of numbers < 10
if (sec < 0) {
sec = "59"
};
return sec;
}
</script>
I can find three errors that hinder your code from performing correctly.
Multiple timers
First off since you invoke both a setInterval in outer scope, and then a setTimeout after each performed iteration, you will end up getting many unwanted timer instances that will do some crazy counting for you.
I recommend you to scrap either one of these and stick with just one of them.
For my example i happend to stick with the setInterval since you're executing the very same method over and over any way.
The initialization
Since the presentTime is declared inside the startTimer-function it will be constantly overwritten with 17 + ":" + 00 (resulting in "17:0" btw).
This is solved by declaring it in the outer scope instead.
Remembering the changes
Finally you need to save the current state of presentTime after modifications. Just adding a presentTime = [m,s].join(":"); at the end of startTimer() solves this.
var presentTime = "17:00";
function startTimer() {
var timeArray = presentTime.split(/[:]+/);
var m = timeArray[0];
var s = checkSecond((timeArray[1] - 1));
if (s == 59) {
m = m - 1
}
var button2 = document.createElement("Button2");
var interval = s;
button2.innerHTML = m + ":" + s;
button2.style = "top:0; left:0rem; height:10% ;color: black; background-color: #ffffff;position:fixed;padding:20px;font-size:large;font-weight: bold;";
document.body.appendChild(button2);
presentTime = [m,s].join(":");
}
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {
sec = "0" + sec
}; // add zero in front of numbers < 10
if (sec < 0) {
sec = "59"
};
return sec;
}
var interval = setInterval(startTimer, 1000);

How to format time with AngularJS filters?

I have a timer that is counting down from a value that is being set by some user.
They enter some number which is being treated as minutes. number * 1000 * 60 to convert it into milliseconds - and from there the counter is counting down and decrements this number.
What I want to do is format this number as minutes and seconds so if the entered number is 3.5, it should be shown as 03:30.
I've tried using the date filter which is provided by Angular but it's not in sync with the value of the timer. It would decrement once and then stops.
Code:
<h4 class="timer">{{ vm.timer.ticks | date: "mm:ss"}}</h4>
The background for the timer is in a service
start(duration) {
this.ticks = duration;
this.timer = this.$interval(() => {
if (this.ticks === 0) return this.stop();
this.ticks--;
}, 1000);
}
I think it is because you are hiding "this" in your inner function. Try to rewrite it as (untested!):
start(duration) {
var ticks = duration;
this.timer = this.$interval(() => {
if (ticks === 0) return this.stop();
ticks--;
}, 1000);
}
Ok, so... I made a terrible mistake when I was calculating the time...
I set up $scope.$watch to watch over my variable from the service and according to that i was changing couple other variables.
$scope.$watch('vm.timer.ticks', value => {
if (value <= 0) {
vm.timer.count = 0;
vm.timer.latest = '';
}
if(vm.timer.ticks > 0) {
seconds = vm.timer.ticks % 60;
minutes = Math.floor(vm.timer.ticks / 60);
filledMinutes = minutes < 10 ? "0" + minutes : minutes;
filledSeconds = seconds < 10 ? "0" + seconds : seconds;
vm.displayTime = filledMinutes + ':' + filledSeconds;
} else {
vm.displayTime = '00:00';
}
});

How to initialize a countdown timer to reflect the user input

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

Does `document.getElementById` break the function?

So I am working on a pomodoro timer and can't figure out what I am doing wrong with my JS. Overview of the project is here : http://codepen.io/Ohillio/pen/wMoNWy
But the specific part of the code I am having issues with is :
// global variables
var min = 0;
var sec = 0;
function tick() {
alert("Counter Started");
sec = 59;
min--;
document.getElementById("timer").innerHTML = min + ":" + sec;
do {
sec--
document.getElementById("timer").innerHTML = min + ":" + sec;
setTimeout(donothing,500);
}
while (sec != 0);
}
The issue is that it seems like it ends the function after the first time through. I want the seconds to tick down to 0, but now it just evaluates to 58.
Does document.getElementById break the function ?
Using setInterval may be a better solution to what you're trying to do. It's going to be more accurate and easy to short circuit once you hit 0. Also, I prefer to keep the total time in seconds and only use minutes for display purposes. I've put together an example for you to review.
<script>
var interval;
var totalSeconds = 1500;
function tick() {
totalSeconds--;
min = Math.floor(totalSeconds / 60);
sec = totalSeconds % 60;
document.getElementById('timer').innerHTML = min + ':' + sec;
if (0 >= totalSeconds) {
clearInterval(interval);
}
}
interval = setInterval(tick, 1000);
</script>
Hope this helps and good luck!
This also should work, with 1 digit second format fix:
var min = 1;
var sec = 30;
function tick() {
document.getElementById("timer").innerHTML = min + ":" + (sec.toString().length < 2 ? '0' : '') + sec;
if(sec === 0 && min > 0) {
min--;
sec = 59;
setTimeout(tick, 500);
} else if (sec > 0) {
sec--;
setTimeout(tick, 500);
} else {
alert("Done!");
}
}
alert("Counter Started");
tick();

Categories