How to make setInterval method have only one instance running - javascript

I have a button that, when clicked, starts a timer and decreases the integer by 1 per second.
However, if the button is clicked multiple times, the integer decreases by a lot more than 1 per second, due to setInterval being called multiple times.
How do I make it so that, even though the button is clicked multiple times, the timer only goes down by 1 each second (I do not want to disable the button).
var time = document.getElementById("time");
document.getElementById("Btn").addEventListener("click", () => {
var decreaseTime = setInterval(() => {
if (time.textContent != 0) {
time.innerHTML = time.textContent - 1;
}
}, 1000);
if (time.textContent == 0) {
clearInterval(decreaseTime);
}
});

You should define your decreaseTime variable in a higher scope and then check if it exists prior to defining your interval. For instance:
var time = document.getElementById("time");
decreaseTime = undefined;
document.getElementById("Btn").addEventListener("click", () => {
if(!decreaseTime){
decreaseTime = setInterval(() => {
if (time.textContent != 0) {
time.innerHTML = time.textContent - 1;
}
}, 1000);
if (time.textContent == 0) {
clearInterval(decreaseTime);
}
}
});

Related

Need Help Coding a Loop Using "For" Statement in JavaScript

I need to make a countdown happen from 10 to 0 using a loop. The loop should replace the need to repeat the code 10X. I also neeed to display the countdown to the user in the HTML. Help!
<script>
function StartTheCountdown()
{
var Countdown = 10;
// Used to keep track of actual time.
// 1000 = 1 second because we are using milliseconds.
var timeout = 10000;
setTimeout(() => {
document.getElementById("CountDownDisplay").innerHTML = "Blastoff!";
Countdown = Countdown - 1;
}, timeout)
timeout = timeout - 1000;
// We need to do this 10 times **************************************
setTimeout(() => {
document.getElementById("CountDownDisplay").innerHTML = Countdown;
Countdown = Countdown - 1;
}, timeout)
timeout = timeout - 1000;
}
</script>
Use setTimeout to repeatedly call the function until count is zero, and then display a message.
// Cache your element
const div = document.querySelector('div');
// Initialise your count to 10
function countdown(count = 10) {
// Update your element textContent
div.textContent = `T-minus: ${count}`;
// Call the function again if the count
// is greater than 0 with a decremented count
if (count > 0) {
setTimeout(countdown, 1000, --count);
// Otherwise update the textContent of the
// element with a message
} else {
div.textContent = 'Blast off!';
}
}
countdown();
<div></div>
Additional documentation
Template/string literals

setInterval goes to negative

I'm buidling this pomodoro app.
https://jsfiddle.net/yvrs1e35/
I got few problems with the timer.
startBtn.addEventListener('click', function(){
minutes.innerHTML = sessionTime.innerHTML - 1
seconds.innerHTML = 59
var timer = setInterval(()=>{
if(Number(minutes.innerHTML) != 0 && Number(seconds.innerHTML) != 0){
seconds.innerHTML--
if(Number(seconds.innerHTML) == 0){
seconds.innerHTML = 59;
minutes.innerHTML--
}
}else if (Number(minutes.innerHTML) == 0 && Number(seconds.innerHTML) == 0){
clearInterval(timer)
}
},1000)
resetBtn.addEventListener('click', function(e){
e.preventDefault();
breakTime.innerHTML = 5
sessionTime.innerHTML = 25
minutes.innerHTML = "00"
seconds.innerHTML = "00"
clearInterval(timer)
})
pauseBtn.addEventListener('click', function(e){
e.preventDefault();
})
})
It works if the timer if there is more than 1 minute left on the interval.
If it goes under 1 minute, even though i have this if in the interval
else if (Number(minutes.innerHTML) == 0 && Number(seconds.innerHTML) == 0){
clearInterval(timer)
}
seconds and minutes go on negative ( after 0:0, timer shows -1:59)
I though that else if statement would stop the interval when both minutes and seconds reach 0, but it doesnt for some reason.
#also if i press startbtn multiple times, the timer starts multiple times, and the seconds go 2x 3x 4x faster, how can I stop the startbtn until the timer reaches 0:0?
Can i get any help?
.innerHTML is an expensive operation. Storing data inside the DOM like this is an antipattern; extracting it and manipulating it stringifies and de-stringifies numbers for no reason. Store state in your JS script and update the DOM content only when a rendering change is necessary. In other words, consider it write-only.
The interval runs multiple times; you'll need a flag to prevent re-triggers (or clearInterval before resetting it). Setting interval to undefined is a good way to indicate that the clock isn't running.
Lastly, setInterval with a cooldown of 1000 is a poor choice for timekeeping. It will drift quite a bit depending on scheduling interruptions and other random factors; the 1000 means "wait at least 1000 milliseconds before firing the callback". Instead, use Date for accuracy.
I'd work entirely in milliseconds and convert to minutes and seconds only for the formatted output. This follows the principle described in the first paragraph about separating presentation from logic.
Here's a proof of concept to illustrate the above points. Of course, if you're doing the pomodoro for fun, sticking to setInterval(() => ..., 1000) does make the code simpler, but I think it's instructive to see it from a couple angles if nothing else.
const padTime = t => (Math.floor(t) + "").padStart(2, 0);
const timeFmt = t => `${padTime(t / 60000)}:${
padTime(t / 1000 % 60)}`;
const run = () => {
interval = setInterval(() => {
if (interval) {
display.textContent = timeFmt(end - Date.now());
}
if (Date.now() >= end) {
clearInterval(interval);
interval = undefined;
}
}, 100);
};
let interval;
let pause;
const initialMinutes = 2;
const duration = initialMinutes * 60000;
const time = Date.now();
let end = time + duration;
const display = document.querySelector("h1");
display.textContent = timeFmt(end - time);
const [startBtn, pauseBtn, resetBtn] =
document.querySelectorAll("button");
startBtn.addEventListener("click", e => {
clearInterval(interval);
if (!interval) {
if (pause) {
end += Date.now() - pause;
pause = undefined;
}
else {
end = Date.now() + duration;
}
}
run();
});
resetBtn.addEventListener("click", e => {
clearInterval(interval);
interval = undefined;
const time = Date.now();
end = time + duration;
display.textContent = timeFmt(end - time);
});
pauseBtn.addEventListener("click", e => {
if (interval) {
pause = Date.now();
clearInterval(interval);
interval = undefined;
}
});
<h1></h1>
<div>
<button>start</button>
<button>pause</button>
<button>reset</button>
</div>
if(Number(minutes.innerHTML) != 0 && Number(seconds.innerHTML) != 0)
Changing the "&&" to "||" should fix one problem (which is that it is stuck at 0:59). If you add another condition to
if(Number(seconds.innerHTML) == 0){
where the if condition is only true, if seconds == 0 AND minutes > 0, then all problems should be solved.
Your negative time comes from starting at 0 and because of this:
minutes.innerHTML = sessionTime.innerHTML - 1
seconds.innerHTML = 59
It changes minutes to negative value and sets seconds to 59. You should add some validation before this and don't start clock.
setInterval doesn't guarantee that your function will execute in the precise interval, just that it wouldn't execute earlier. This way, on a slow/loaded computer, the function could be called after the interval is already elapsed.
In other words, you probably wish to check if the timer has already elapsed, not if it's just about to do so.
(Number(minutes.innerHTML) <= 0 && Number(seconds.innerHTML) <= 0)

How can i display the right numbers in my counting

I want to count down a counter. However, it always starts one number later. I know the problem, but I can't find the solution.
function countStart() {
const countdown = setInterval(() => {
time -= 1;
document.getElementById('counter').textContent = time;
if (time < 1) {
clearInterval(countdown);
}
}, 1000);
}
If I count down from 10. If it starts at 9, how do I change that?
You are decreasing your "time" variable before you display it. So if your "time" variable has a value of 10 initially, it will get reduced to 9 before being displayed due to the statement "time -=1" occuring before setting it as textContent for your element (and thus displaying it).
Try decreasing your "time" variable after setting it as textContent of your counter element.
function countStart() {
const countdown = setInterval(() => {
document.getElementById('counter').textContent = time;
time -= 1;
if (time < 1) {
clearInterval(countdown);
}
}, 1000);
}
you just put time-=1 below the line where you change DOM
function countStart(time) {
const countdown = setInterval(() => {
//document.getElementById('counter').textContent = time;
console.log(time)
time -= 1;
if (time < 1) {
clearInterval(countdown);
}
}, 1000);
}
just changed dom manipulation to console.log

Simple Javascript Countdown how to update global from setintervals local function

I am pulling countdown data(just a number like 10,15..) from db and making it equal to global.
just under class.
countdown:any;
console.log(this.countdown);//i got data 10 sec
let downloadTimer=setInterval(function counter(countdown){
countdown--; // how to update this.countdown ?
console.log(countdown);
if(countdown <= 0){
clearInterval(downloadTimer);
console.log("time is up!");
}
},1000,this.countdown);
I am starting with sending this.countdown to counter parameter but then how do i update this.countdown from local counter function? because when setinterval iterates it always calls this.countdown so counter stucks at first this.countdown value.
If i do it without parameter i cant access this.counter from counter local function.
You are trying to pass a single scalar value, this.countdown, which is does, but you can't update that the same way you can an object or array and get the side effect of updating the original.
You could pass a reference to this and then use it to update the original:
class test {
constructor() {
this.countdown = 20
}
doit() {
console.log(this.countdown); //i got data 10 sec
let downloadTimer = setInterval(function counter(obj) {
obj.countdown--; // how to update this.countdown ?
console.log(obj.countdown);
if (obj.countdown <= 0) {
clearInterval(downloadTimer);
console.log("time is up!");
}
}, 200, this);
}
}
var t = new test
t.doit()
You could also use an arrow function to capture the value of this rather than passing it in:
class test {
constructor() {
this.countdown = 20
}
doit() {
console.log(this.countdown); //i got data 10 sec
let downloadTimer = setInterval(() => {
this.countdown--; // how to update this.countdown ?
console.log(this.countdown);
if (this.countdown <= 0) {
clearInterval(downloadTimer);
console.log("time is up!");
}
}, 200);
}
}
var t = new test
t.doit()

Javascript call function at each "X" minutes by clock

Ok, so I have:
function show_popup() {
alert('Ha');
}
Now, what I want is to call this function at each X minutes BUT giving as reference the clock (the real time).
If X is 5, then the next function works properly:
setInterval(function(){
var date = new Date();
var minutes = date.getMinutes().toString();
var minutes = minutes.slice(-1); // Get last number
if(minutes == 0 || minutes == 5)
{
show_popup(); // This will show the popup at 00:00, 00:05, 00:10 and so on
}
}, 1000);
How can I make this function to work if I change 5 minutes to 4, or to 3, or to 20 ?
I must mention that I can't change the timer from setinterval, cause this it will mean that the popup will trigger only if you are on page AFTER passing X minutes. I don't want that. I want to show the popup at specific minutes giving the reference the clock.
You need to find the multiples of X
To do that, you can use modulo operation, so:
if(minutes % X === 0) {
show_popup();
}
The modulo operation will return the rest of division between a and b, if thats 0, thats means b is multiple of a.
For example, if you want to show every 3 minutes:
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0 //show
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0 //show
And so on...
two ways, just run the code to see results(in chrome browser)
1.use timer and you can change period when next tick comes, timer is not that precise
class MyInterval {
constructor(defaultInterval, callback) {
this.interval = defaultInterval
this.callback = callback
this._timeout = null
this.tick()
}
tick() {
const {
interval,
callback
} = this
this._timeout = setTimeout(() => {
callback()
this.tick()
}, interval)
}
stop() {
clearTimeout(this._timeout)
}
changeInterval(interval) {
this.interval = interval
}
}
const myInterval = new MyInterval(1000, () => console.log(new Date()))
setTimeout(() => {
myInterval.changeInterval(2000)
}, 3500)
setTimeout(() => {
myInterval.stop(2000)
}, 13500)
2.use a minimal interval, more quick to react, has a minimal limit, may cost more
class MyInterval {
constructor(minimal, defaultInterval, callback) {
this.minimal = minimal
this.interval = defaultInterval
this.callback = callback
this._current = 0
this._timeout = setInterval(() => {
this._current++
if (this._current >= this.interval) {
this._current = 0
callback()
}
}, minimal)
}
stop() {
clearInterval(this._timeout)
}
changeInterval(interval) {
this.interval = interval
}
}
const myInterval = new MyInterval(1000, 1, () => console.log(new Date()))
setTimeout(() => {
myInterval.changeInterval(2)
}, 3500)
setTimeout(() => {
myInterval.stop()
}, 13500)

Categories

Resources