Javascript timers inside a function, clearTimeout doesnt seem to work - javascript

Im having trouble getting javascript timers to work as intended when updating an old site to be more modern, it used to use iframes so refreshing the timers wasn't an issue, but now i want to use ajax to reload the div containing the timer, however it keeps counting at double speed, ive been reading about using clearTimeout which seems to be the solution im looking for, however i just can not get it to work at all, could anyone give me any advice on where to look to stop the times counting at double speed
<script type="text/javascript">
var var1 = 60; // time in seconds to count down
function timer1() {
if (var1 > 0) {
var1 = var1 - 1;
}
else {
var1 = "<font color=\"#90ee90\">GO</font>";
}
var countdown = document.all ? document.all["timer1"] : document.getElementById ? document.getElementById("timer1") : "";
if (countdown) {
countdown.innerHTML = var1 + " ";
setTimeout( 'timer1()', 1000 );
}
}
timer1();
</script>
I have already attempted var stop= setTimeout('<?=$id; ?>()',1000);
And then calling clearTimeout(stop);
However this seemed to completely ignore clearTimeout so i felt like I must have been doing it wrong

You probably want to use setInterval() in this case.
In case you want types: TS Playground link
#timer1 {
background-color: #000;
color: #fff;
font-family: monospace;
font-size: 2rem;
padding: 1rem;
}
<div id="timer1"></div>
<script type="module">
const tickDelay = 1000;
let seconds = 5;
let timerId = 0;
// To stop the timer, just call this function
function stopTimer () {
clearInterval(timerId);
console.log('Timer done');
}
function tick () {
const element = document.getElementById('timer1');
if (!element) return;
if (seconds > 0) {
element.textContent = String(seconds);
seconds -= 1;
}
else {
element.style.setProperty('color', '#90ee90');
element.textContent = 'GO';
stopTimer();
}
}
timerId = setInterval(tick, tickDelay);
</script>

Related

The new value for the (if) condition has no effect

I have a JS code block just like below. the code will redirect user to login page after 30 sec but if the user clicks anywhere on the page the process will be reset. theoretically the code logic is true setInterval and EventListener both works fine but when I click somewhere in the page although global variable (counter) changes to 0 but (if) condition in the setInterval has no effects and still (counter) variable remain unchanged and the redirection will be happened!!!
var counter = 0;
setInterval(function() {
if (counter >= 30) {
// commented below line for testing with run code in stack overflow.
// In real code it will be uncommented.
// document.location = 'http://localhost/login';
console.log('redirect');
} else {
counter++;
}
}, 1000);
document.addEventListener('click', function(e) {
counter = 0;
}, true);
Your code should work
Here is a perhaps more understandable version using clearInterval
let secs = 5;
let tId;
function int() {
clearInterval(tId);
counter = secs;
tId = setInterval(function() {
if (counter <= 0) {
document.location = 'https://github.com';
} else {
counter--;
}
document.getElementById("x").innerText = counter
}, 1000);
}
document.addEventListener('click', int);
int()
<span id="x"></span>

How can I pause and resume a setInterval function with Jquery/JS within a loop? [duplicate]

How do I pause and resume the setInterval() function using Javascript?
For example, maybe I have a stopwatch to tell you the number of seconds that you have been looking at the webpage. There is a 'Pause' and 'Resume' button. The reason why clearInterval() would not work here is because if the user clicks on the 'Pause' button at the 40th second and 800th millisecond, when he clicks on the 'Resume' button, the number of seconds elapsed must increase by 1 after 200 milliseconds. If I use the clearInterval() function on the timer variable (when the pause button is clicked) and then using the setInterval() function on the timer variable again (when the resume button is clicked), the number of seconds elapsed will increase by 1 only after 1000 milliseconds, which destroys the accuracy of the stopwatch.
So how do I do that?
You could use a flag to keep track of the status:
var output = $('h1');
var isPaused = false;
var time = 0;
var t = window.setInterval(function() {
if(!isPaused) {
time++;
output.text("Seconds: " + time);
}
}, 1000);
//with jquery
$('.pause').on('click', function(e) {
e.preventDefault();
isPaused = true;
});
$('.play').on('click', function(e) {
e.preventDefault();
isPaused = false;
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="play">Play</button>
<button class="pause">Pause</button>
This is just what I would do, I'm not sure if you can actually pause the setInterval.
Note: This system is easy and works pretty well for applications that don't require a high level of precision, but it won't consider the time elapsed in between ticks: if you click pause after half a second and later click play your time will be off by half a second.
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value. So there is no need to pause timer and you will get best possible accuracy in this way.
While #Jonas Giuro is right when saying that:
You cannot PAUSE the setInterval function, you can either STOP it (clearInterval), or let it run
On the other hand this behavior can be simulated with approach #VitaliyG suggested:
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value.
var output = $('h1');
var isPaused = false;
var time = new Date();
var offset = 0;
var t = window.setInterval(function() {
if(!isPaused) {
var milisec = offset + (new Date()).getTime() - time.getTime();
output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));
}
}, 10);
//with jquery
$('.toggle').on('click', function(e) {
e.preventDefault();
isPaused = !isPaused;
if (isPaused) {
offset += (new Date()).getTime() - time.getTime();
} else {
time = new Date();
}
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="toggle">Toggle</button>
Why not use a simpler approach? Add a class!
Simply add a class that tells the interval not to do anything. For example: on hover.
var i = 0;
this.setInterval(function() {
if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
console.log('Counting...');
$('#counter').html(i++); //just for explaining and showing
} else {
console.log('Stopped counting');
}
}, 500);
/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
$(this).addClass('pauseInterval');
},function() { //mouse leave
$(this).removeClass('pauseInterval');
}
);
/* Other example */
$('#pauseInterval').click(function() {
$('#counter').toggleClass('pauseInterval');
});
body {
background-color: #eee;
font-family: Calibri, Arial, sans-serif;
}
#counter {
width: 50%;
background: #ddd;
border: 2px solid #009afd;
border-radius: 5px;
padding: 5px;
text-align: center;
transition: .3s;
margin: 0 auto;
}
#counter.pauseInterval {
border-color: red;
}
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="counter"> </p>
<button id="pauseInterval">Pause</button></p>
I've been looking for this fast and easy approach for ages, so I'm posting several versions to introduce as many people to it as possible.
i wrote a simple ES6 class that may come handy.
inspired by https://stackoverflow.com/a/58580918/4907364 answer
export class IntervalTimer {
callbackStartTime;
remaining = 0;
paused = false;
timerId = null;
_callback;
_delay;
constructor(callback, delay) {
this._callback = callback;
this._delay = delay;
}
pause() {
if (!this.paused) {
this.clear();
this.remaining = new Date().getTime() - this.callbackStartTime;
this.paused = true;
}
}
resume() {
if (this.paused) {
if (this.remaining) {
setTimeout(() => {
this.run();
this.paused = false;
this.start();
}, this.remaining);
} else {
this.paused = false;
this.start();
}
}
}
clear() {
clearInterval(this.timerId);
}
start() {
this.clear();
this.timerId = setInterval(() => {
this.run();
}, this._delay);
}
run() {
this.callbackStartTime = new Date().getTime();
this._callback();
}
}
usage is pretty straightforward,
const interval = new IntervalTimer(console.log('aaa'), 3000);
interval.start();
interval.pause();
interval.resume();
interval.clear();
My simple way:
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
How to use:
let seconds = 0
const timer = new Timer(() => {
seconds++
console.log('seconds', seconds)
if (seconds === 8) {
timer.clear()
alert('Game over!')
}
}, 1000)
timer.pause()
console.log('isPaused: ', timer.paused)
setTimeout(() => {
timer.resume()
console.log('isPaused: ', timer.paused)
}, 2500)
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
The code is written quickly and did not refactored, raise the rating of my answer if you want me to improve the code and give ES2015 version (classes).
I know this thread is old, but this could be another solution:
var do_this = null;
function y(){
// what you wanna do
}
do_this = setInterval(y, 1000);
function y_start(){
do_this = setInterval(y, 1000);
};
function y_stop(){
do_this = clearInterval(do_this);
};
The following code, provides a precision way to pause resume a timer.
How it works:
When the timer is resumed after a pause, it generates a correction cycle using a single timeout, that will consider the pause offset (exact time when the timer was paused between cycles). After the correction cycle finishes, it schedules the following cycles with a regular setInteval, and continues normally the cycle execution.
This allows to pause/resume the timer, without losing the sync.
Code :
function Timer(_fn_callback_ , _timer_freq_){
let RESUME_CORRECTION_RATE = 2;
let _timer_statusCode_;
let _timer_clockRef_;
let _time_ellapsed_; // will store the total time ellapsed
let _time_pause_; // stores the time when timer is paused
let _time_lastCycle_; // stores the time of the last cycle
let _isCorrectionCycle_;
/**
* execute in each clock cycle
*/
const nextCycle = function(){
// calculate deltaTime
let _time_delta_ = new Date() - _time_lastCycle_;
_time_lastCycle_ = new Date();
_time_ellapsed_ += _time_delta_;
// if its a correction cicle (caused by a pause,
// destroy the temporary timeout and generate a definitive interval
if( _isCorrectionCycle_ ){
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
_isCorrectionCycle_ = false;
}
// execute callback
_fn_callback_.apply( timer, [ timer ] );
};
// initialize timer
_time_ellapsed_ = 0;
_time_lastCycle_ = new Date();
_timer_statusCode_ = 1;
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
// timer public API
const timer = {
get statusCode(){ return _timer_statusCode_ },
get timestamp(){
let abstime;
if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );
else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );
return abstime || 0;
},
pause : function(){
if( _timer_statusCode_ !== 1 ) return this;
// stop timers
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
// set new status and store current time, it will be used on
// resume to calculate how much time is left for next cycle
// to be triggered
_timer_statusCode_ = 2;
_time_pause_ = new Date();
return this;
},
resume: function(){
if( _timer_statusCode_ !== 2 ) return this;
_timer_statusCode_ = 1;
_isCorrectionCycle_ = true;
const delayEllapsedTime = _time_pause_ - _time_lastCycle_;
_time_lastCycle_ = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );
_timer_clockRef_ = setTimeout( nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);
return this;
}
};
return timer;
};
let myTimer = Timer( x=> console.log(x.timestamp), 1000);
<input type="button" onclick="myTimer.pause()" value="pause">
<input type="button" onclick="myTimer.resume()" value="resume">
Code source :
This Timer is a modified and simplified version of advanced-timer, a js library created by myself, with many more functionalities.
The full library and documentation is available in NPM and GITHUB
let time = document.getElementById("time");
let stopButton = document.getElementById("stop");
let timeCount = 0,
currentTimeout;
function play() {
stopButton.hidden = false;
clearInterval(currentTimeout);
currentTimeout = setInterval(() => {
timeCount++;
const min = String(Math.trunc(timeCount / 60)).padStart(2, 0);
const sec = String(Math.trunc(timeCount % 60)).padStart(2, 0);
time.innerHTML = `${min} : ${sec}`;
}, 1000);
}
function pause() {
clearInterval(currentTimeout);
}
function stop() {
stopButton.hidden = true;
pause();
timeCount = 0;
time.innerHTML = `00 : 00`;
}
<div>
<h1 id="time">00 : 00</h1>
<br />
<div>
<button onclick="play()">play</button>
<button onclick="pause()">pause</button>
<button onclick="stop()" id="stop" hidden>Reset</button>
</div>
</div>

I'm trying to set an Interval inside a setInterval, but it is Accelerating

var i = 0
function fill(){
setInterval(animation,30) // the problem starts here.
function animation(){
if (i<100){
i++
load.style.width = i+'%'
} else {
i = 0
}
}
clearInterval(animation,30) // this should stop the interval above, but it is accelerating. Anyone nows how to solve it?
}
fill()
setInterval(fill,3000)
It's accelerating because you don't correctly stop the previous interval. So every time you call fill() it adds another interval function that's updating the progress bar.
You need to save the result of setInterval in a variable, and use that in the clearInterval call.
You should call clearInterval when you reach the 100% limit, not immediately after calling setInterval.
var i = 0
function fill() {
var interval = setInterval(animation, 30) // the problem starts here.
function animation() {
if (i < 100) {
i++
load.style.width = i + '%'
} else {
i = 0
clearInterval(interval)
}
}
}
fill()
setInterval(fill, 3000)
#load {
background-color: blue;
height: 30px;
}
<div id="load"></div>

Wrote two JavaScript functions but one of them seems to be crashing my website

So I wrote a simple program that should change the background of a given website after 3 seconds.
Now this is my JavaScript code:
//this function changes the backgrounds after 3 seconds and increments n
function changebackgroundTimed(startvariable)
{
var n = startvariable;
var loop = 1;
while (loop == 1)
{
setTimeout(function(){changebackground(n)}, 3000)
n++;
if (n == 5)
{
n=1;
}
}
}
//this function changes the background depending on the given number
function changebackground(number)
{
if (number == 1)
{
$("body").css("background","no-repeat center/120%url('../images/1.jpg')");
}
else if (number == 2)
{
$("body").css("background","no-repeat center/120%url('../images/2.jpg')");
}
else if (number == 3)
{
$("body").css("background","no-repeat center/120%url('../images/3.jpg')");
}
else if (number == 4)
{
$("body").css("background","no-repeat center/120%url('../images/4.jpg')");
}
else {
$("body").css("background","no-repeat center/120%url('../images/1.jpg')");
}
}
in the html I just call it with: changebackgroundTimed(2);
Problem is: When I start the page it just loads for a long while and then eventually crashes while showing nothing. It has to do something with these two functions. Does anybody of you notices a mistake I may be missing?
Looks like you are not updating your "loop" variable, which is causing it go in an infinite loop.
Instead of using the while loop, use setInterval() method. That should do the work for you.
Keep the variable n outside the function, and refer it using outsiders this keyword.
function abc(){
var self = this;
self.n = 1;
setInterval(function () {
if(self.n ===1){
self.n = self.n + 1;
// your code
}else if(){
// and so on
}
changebackground(self.n);
},3000);
}
my 2 cents...
CSS:
:root {
--bg-images :"../images/1.jpg|../images/2.jpg|../images/3.jpg|../images/4.jpg|../images/5.jpg";
--img-bg :url(../images/1.jpg);
}
body {
background-image: var(--img-bg);
background-position: center/120%;
background-repeat: no-repeat;
}
Javascript:
const Root = document.documentElement
, gRoot = getComputedStyle(Root)
, imgList = gRoot.getPropertyValue('--bg-images').match(/"(.*?)"/)[1].split('|')
, NbImgs = imgList.length
, regExp = /\(([^)]+)\)/ // to get img url between parentheses
;
let currImg = imgList.findIndex(img=>img=== (regExp.exec(gRoot.getPropertyValue('--img-bg'))[1]))
;
setInterval(() => {
currImg = ++currImg % NbImgs;
Root.style.setProperty('--img-bg', `url(${imgList[currImg]})`)
}, 3000)
It appears that you never exit your while loop which makes the page crash as it runs forever. At some part in your code you have to change the value of the loop variable.
You are creating an infinite loop and never breaking out of it so it will run as fast as possible and lock up the UI.
Why not use setInterval like so:
const backgroundChanger = setInterval(changeBackground, 3000)
let background = 1
function changeBackground() {
if (background >= 5) background = 1
// set background however you like
document.body.style.background = 'url(../images/' + background++ + '.jpg) center/120% no-repeat'
}
setTimeout is a non-blocking call. This means that your code wont wait for 3000ms and keep on running in an infinte loop while calling changebackground(n);.
To read more about setTimeout go here setTimeout from MDN
Use the following code:
function changebackgroundTimed(startvariable)
{
var n = startvariable;
setInterval(() => {
changebackground(n);
n = (n % 5) + 1;
}, 3000);
}
//this function changes the background depending on the given number
function changebackground(number)
{
console.log(number)
}
changebackgroundTimed(2)
var i = 1;
setInterval(() => {
if(i > 5){
i = 0;
}else{
changeBackground(i)
}
i++;
}, 250);
function changeBackground(i){
switch(i){
case 1 :
$("body").css("color", "red")
break;
case 2 :
$("body").css("color", "blue")
break;
case 3 :
$("body").css("color", "green")
break;
case 4 :
$("body").css("color", "black")
break;
case 5 :
$("body").css("color", "orange")
break;
default:
$("body").css("color", "white")
}
}
A variation on a theme perhaps but without the hardcoded limitation of the original functions
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Background image cycling</title>
<style>
body{
width:100%;
height:100vh;
padding:0;
margin:0;
}
</style>
<script>
/* configuration */
const PAUSE=3;
const SLIDES=['//www.stevensegallery.com/800/600','//www.placecage.com/800/600','//placebear.com/640/360','//picsum.photos/800/600'];
(function( delay ){
document.addEventListener('DOMContentLoaded',()=>{
var images=[/* default images */
'//placekitten.com/900/800',
'//placekitten.com/1000/800',
'//placekitten.com/1024/768',
'//placekitten.com/1200/800',
'//placekitten.com/1366/768'
];
if( arguments[1] )images=images.concat( arguments[1] )
var i=0;
(function(){
setTimeout( arguments.callee, 1000 * delay );
i++;
if( i > images.length - 1 )i=0;
document.body.style.background='center / contain no-repeat url('+images[ i ]+')';
})();
});
})( PAUSE, SLIDES );
</script>
</head>
<body>
<!-- content -->
</body>
</html>

How to get clearInterval() to work in a loop in JavaScript

So I'm trying to get a function to run once every second, and then after four seconds I want it to stop using clearInterval()
function dotdotdot(){
var x = 0;
setInterval(function(){
if (x>=3){
torpWri = torpWri + ".";
document.getElementById("torpTxt").innerHTML = torpWri;
x++;
}
else{
x = 0;
clearInterval();
}
},1000);
}
This is my function and it should stop after four seconds and then reset x to 0 for when I call it again.
function loadButton(){
torpWri = "Torpedo Loading"
if(torpLoadAmount[arNum]<5){
torpLoadAmount[arNum]++;
torpAmount--;
document.getElementById("torpCnt").innerHTML = torpAmount;
document.getElementById("torpTxt").style.visibility = "visible";
document.getElementById("butunload").disabled=true;
document.getElementById("butfire").disabled=true;
document.getElementById("torpTxt").innerHTML = torpWri;
dotdotdot();
}
else{
document.getElementById("torpTxt").style.visibility = "visible";
document.getElementById("torpTxt").innerHTML = "Torpedo Bay Full";
}
timer3();
}
This is how I'm calling it.
I'm just needed to know why it isn't running the function dotdotdot(); every second and then stopping after four. Then when I call it again it should all just reset. But it's not running...
I've been searching for a while and haven't found anything, so I came here.
(Also, please don't comment on my other code, I know there are probably easier ways to do it, but this is what I'm working with right now.)
setInterval returns a timerID, which needs to be passed to clearInterval.
var ticks = 0;
var intervalID = setInterval(function() {
if (++ticks == 4) {
clearInterval(intervalID);
}
}, 1000);
You could also use setTimeout instead, and just not schedule a new tick when the condition is met.
setTimeout(function callback(ticks) {
if (ticks > limit) {
return;
}
setTimeout(callback, 0, ++ticks);
}, 1000, 0)
You need to store the handle / intervalId for the interval when it is set and then use it when you want to clear the interval:
function dotdotdot(){
var x = 0;
var intervalId = -1;
intervalId = setInterval(function(){
if (x>=3){
torpWri = torpWri + ".";
document.getElementById("torpTxt").innerHTML = torpWri;
x++;
} else {
x = 0;
clearInterval(intervalId);
}
},1000);
}
More info: https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Timers
setInterval will return a timerid. So do like
var timer = setInterval(fun......)
Then
clearInterval(timer)

Categories

Resources