start Interval again after clearing it - javascript

How can I restart Interval after clearing it? I want a button to be clickable once every 11 seconds I made it disabled while timer is > 0 after it's equal to 0 button isn't disabled so it's clickable I wrote this code it seems to work but after multiple call of a setInterval() function timer goes down too fast any soltuions>
data:{
sTimer:11,
sDisabled:true,
asd:null
},
methods:{
testing(){
this.sTimer--;
if(this.sTimer == 0){
clearInterval(this.asd);
this.sTimer= 11;
this.sDisabled = false;
}else{
this.sDisabled = true;
}
},
specialAttack(){
setInterval(() => this.testing(), 1000)
}
},
created(){
this.asd = setInterval(() => this.testing(), 1000);
}
<button class="specialAttack" :disabled="sDisabled" #click="specialAttack(); testing()">Special Attack {{ sTimer }}</button>

There are several mistakes you made here, so first of all to achieve such a thing you should use setTimeout instead of setInterval because interval generally will be repeated after a certain given time, so this may one of the possible reason for being called twice when you click your button as well. Then you need to create a generic function for this cause and then call it in document creation instead of creating two different intervals and calling them separately.
So with respecting these rules, your final code should be something like this:
HTML
<button class="specialAttack" :disabled="sDisabled" #click="specialAttack();">Special Attack {{ sTimer }}</button>
Javascript
data: () => {
return {
sTimer: 11,
sDisabled: false,
asd: null
};
},
methods: {
specialAttack() {
clearTimeout(this.asd); // Or as you suggest in comments you can disbale it here with replacing this: this.sDisabled = true
if (this.sTimer > 0) {
this.asd = setTimeout(() => {
this.sDisabled = true;
this.sTimer -= 1;
this.specialAttack();
}, 1000);
} else {
clearTimeout(this.asd);
this.sTimer = 11;
this.sDisabled = false;
}
}
},
created() {
this.specialAttack();
}
Here is a working demo:

Related

VueJS : How to update value inside watcher without affecting the watcher

I made an online quiz application using Vue in which each question has its own duration. I'm using watcher for the countdown and show it on the webpage. The problem I'm facing is every time I updated the time to match the next question's time, the countdown will end much sooner. What can I do to update the time limit in the watcher without interrupting the current countdown?
watch: {
timerEnabled(value) {
if (value) {
setTimeout(() => {
this.timeLimit--;
}, 1000);
}
},
timeLimit: {
handler(value) {
if (value >= 0 && this.timerEnabled) {
setTimeout(() => {
this.timeLimit--;
}, 1000);
}
else{
// set the timer to the next question's time limit and move to the next question
}
}
}
}
I think you can try something like this:
data() {
return {
timer: null,
timerCounter: 0
}
},
methods: {
setTimer(state) { // true enabled, false disabled
if(state) {
this.timerCount = // seconds
this.timer = setTimeout(() => {
timerCounter--;
if(timerCounter <= 0) {
this.setTimer(false);
// go to next question
this.setTimer(true);
}
}, 1000);
} else {
clearTimeout(this.timer);
}
}
}
You only need to call setTimer(true) for start the timer for the first time, for example in the mounted() function or at click event of a button.
If you don't want that the timer restart after question change, remove this.setTimer(true);.

How to set interval only on entering specific section with scroll and clear it immediately?

There is this code below, which is pretty straightforward:
User scrolls into some section and that section is flagged with a CSS class "view"
When I get to that specific section "someClsOfSection" I want to trigger adding 20 different classes to some element with setInterval
Whenever I scroll the condition will be fulfilled and I don't know how to clear the interval once I enter this section...
https://codepen.io/StevaNNN/pen/GRNOLyJ here is the codepen for
const isFullySeen = el =>
el && typeof el.getBoundingClientRect === 'function'
&& el.getBoundingClientRect()['top'] +
window.scrollY + (window.innerHeight) <= window.innerHeight + window.scrollY;
$(document).ready(function () {
$(window).scroll(function () {
$('.section').each(function() {
if (isFullySeen(this) === true) {
$(this).addClass('in-view');
}
if(isFullySeen(this) && $(this).hasClass('.someClsOfSection')) {
let tempCls = 0;
let toTwentyPercent = setTimeout(function () {
setInterval(function () {
if (tempCls < 20) {
tempCls++;
$('.c100').addClass(`p${tempCls}`).animate();
}
}, 100);
}, 1000);
clearInterval(toTwentyPercent);
}
});
});
});
Actually, the setTimeout() is assigned to toTwentyPercent... But is cleared right after. So it never execute its callback. Additionnaly, it's the setInterval() that you wish to clear. So here is what I suggest you to try:
let tempCls = 0;
setTimeout(function () {
let toTwentyPercent = setInterval(function () {
if (tempCls < 20) {
$('.c100').eq(tempCls).addClass(`p${tempCls}`);
tempCls++;
}else{
clearInterval(toTwentyPercent);
}
}, 100);
}, 1000);
So here, the script will wait 1 second, then will assign a setInterval() to toTwentyPercent.
Then, every 100 ms, a class will be added to a .c100 element. Notice the .eq(tempCls) to add the p0, p1, p2... in sequence. Without the .eq() method, all .c100 element will get an added class on each interval... And on the last interval, they will all have the classes from p0 to p19.
Finally, when tempCls < 20 is false, that is the time to clear the interval.
I removed the .animate() because I don't know what is the intend. That method need at least one property to animate to a new value. It is pretty useles without any argument.

endless while loop after mousemove

I am going crazy here.
I want to show an element on mouse move, and hide it 10 sec after last move of the mouse.
I wrote this:
document.addEventListener("DOMContentLoaded", function(event) {
var time = 0;
document.addEventListener("mousemove", function(event) {
console.log('$');
document.getElementsByClassName("mybar")[0].style.visibility = 'visible';
time = 0;
while (time < 11) {
setTimeout(function() {
time++
}, 1000);
console.log(time, time == 10);
if (time == 10) {
document.getElementsByClassName("mybar")[0].style.visibility = 'hidden';
}
}
});
});
<div class='mybar'>
<h1> TESTING </h1>
</div>
Why does it end up in an endless loop?
Why doesn't it exit on condition? why does the if never gets the 'true' parameter?
Notice : don't run it this way... it will kill your tab.
First, you don't need to wait for DOMContentLoaded to add an event listener to document, since if you did, you couldn't add DOMContentLoaded in the first place.
The infinite loop is because setTimeout doesn't pause the script. It schedules its callback for the time you provide, and irrespective of that time, the callbacks will not run until the current running code in the thread completes, which never happens because you don't increment the time variable.
So the loop never ends, and so the thread is never made available, so your callbacks never can run, so time can never be incremented.
Lastly, starting a setTimeout inside an event handler that shares a local variable and executes very rapidly on an event like mousemove is prone to give unexpected results. For example, in your code, every time the handler runs, it'll reset time to 0, which doesn't seem to be what you'd want.
A solution would be to ditch the loop, schedule the visibility for 10 seconds, and prevent the main part of the code in the handler from running in the meantime by using a boolean variable.
var timer = null;
document.addEventListener("mousemove", function(event) {
var myBar = document.querySelector(".mybar");
if (!myBar) {
return; // there's no mybar element
}
if (timer == null) {
myBar.style.visibility = 'visible';
} else {
clearTimeout(timer); // clear the currently running timer
}
// set to hidden in 10 seconds
timer = setTimeout(function() {
myBar.style.visibility = 'hidden';
timer = null; // clear the timer
}, 10000);
});
I also switched to querySelector instead of getElementsByClassName because it's shorter and cleaner. And I used a variable to make sure the element is found before setting the style.
You need a flag out of the mousemove scope that tells your listener that you've already ran.
if(running) return;
running = true;
In context:
document.addEventListener("DOMContentLoaded", function(event) {
var time = 0;
var running = false;
document.addEventListener("mousemove", function(event) {
console.log('$');
if(running) return;
running = true;
document.getElementsByClassName("mybar")[0].style.visibility = 'visible';
time = 0;
while (time < 11) {
setTimeout(function() {
time++
}, 1000);
console.log(time, time == 10);
if (time == 10) {
document.getElementsByClassName("mybar")[0].style.visibility = 'hidden';
}
}
});
});
Here's a way to do it with regular JavaScript. If your browser isnt ES6 compliant, you can replace the arrow functions with regular function expressions. The example hides the text after 2 seconds instead of 10, just so you can see it work without having to waste 8 extra seconds.
//hide by default
document.getElementById('myBar').style.display = 'none';
var timer = null;
var hideDivTimer = () => {
timer = setTimeout(() => {
document.getElementById('myBar').style.display = 'none';
}, 2000);
};
document.addEventListener('mousemove', () => {
clearTimeout(timer);
document.getElementById('myBar').style.display = 'inline';
hideDivTimer();
});
<body>
<div id='myBar'>
<h1> TESTING </h1>
</div>
</body>

clearInterval() not working in an if/else statement with recursive function

I am trying to build a countdown that switches between play time and work time. It starts with 10 seconds of work then when it reaches 0 seconds we clear the interval and set it to 'play' and call the function again...
var model = {
intervalID: 0,
status: 'work',
workSeconds: 10,
playSeconds: 10,
}
function startCountdown(){
if (model.status === 'work') {
model.countdown = model.workSeconds;
model.intervalID = setInterval(function(){
model.countdown--;
if (model.countdown > 0) {
console.log('work', model.countdown);
} else {
model.status = 'play';
clearInterval(model.indervalId);
startCountdown();
}
}, 1000);
} else if (model.status === 'play') {
model.countdown = model.playSeconds;
model.intervalID = setInterval(function(){
model.countdown--;
if (model.countdown > 0) {
console.log('play', model.countdown);
} else {
model.status = 'work';
clearInterval(model.indervalId);
startCountdown();
}
}, 1000);
}
}
startCountdown();
This function is supposed to console log a 10 second countdown for work, then when the countdown reaches 0 switch to a 10 seconds countdown of play, and keep doing that.
At the moment it looks like no intervals are being cleared and both intervals just keep setting each other in a feedback loop.
Why are the intervals not being cleared?
I think you have a typo. clearInterval(model.indervalId); should be clearInterval(model.intervalID);.
I'd use setTimeout for this too, but I guess you already know about that.

Duplicated countdown timer after click event

I have this function:
var secondsRemaining = 45;
function countdown(secondsRemaining) {
var seconds = secondsRemaining;
if (secondsRemaining > 0) {
$('.timer > div').html(seconds);
secondsRemaining--;
} else {
if (secondsRemaining == 0) {
// Times up ....
}
}
setInterval(function() {
countdown(secondsRemaining);
}, 1000);
}
I am running the function in the Document ready function with:
countdown(secondsRemaining);
and also I run it again after I clicked for answer.
the problem is that I have 2 countdown timer now running simultaneously, new one that start from 45 seconds and the old one that continue from where it was.
Use clearInterval()
First, store the interval id of the first setInterval():
var secondsRemaining = 45;
function countdown(secondsRemaining) {
var seconds = secondsRemaining;
if (secondsRemaining > 0) {
$('.timer > div').html(seconds);
secondsRemaining--;
} else {
if (secondsRemaining == 0) {
// Times up ....
}
}
myinterval=setInterval(function() { //myint now contains the interval id
countdown(secondsRemaining);
}, 1000);
}
Now, when the user clicks, before setting up a new interval, cancel the first one with clearInterval():
button.onclick=function(){
//do stuff
clearInterval(myinterval)
setInterval(function() {
countdown(secondsRemaining);
}, 1000);
// do stuff
}
Sorry I don't really understand the way you have phrased the question seems to say that you intentionally want to start two time events, one when the document has loaded and the other when you click answer which are both outputting to the same element.
If the problem is you want it to have the output in two different elements then you could pass an argument of the element you could use:
countdown($('.pick-me'), secondsRemaining);
If you want to use the same element, then you could always clear the interval on each click and then set the interval for the new event, just got the update and seen Manishearths response so wont go into any more detail about it as that's probably the answer you are looking for.

Categories

Resources