setInterval() acts weird after switching tabs - javascript

I have created an effect using javascript to show top categories in my project.
As you can see in the image above, it works perfectly fine. But after running for sometime; If the user leaves this page and switched to another tab and come back to this after sometime, then it starts to act weird.
Below is the code which I'm using to make this effect.
var curCat = 0;
var cats = [
"<a href='/search/?cat=1'>Animals</a>",
"<a href='/search/?cat=2'>Graffiti</a>",
"<a href='/search/?cat=3'>Figures</a>",
"<a href='/search/?cat=6'>Landscape</a>",
"<a href='/search/?cat=7'>Portrait</a>",
"<a href='/search/?cat=9'>Other</a>"
];
function catSlider() {
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
setInterval(function() {
$(catDisplay).fadeOut();
setTimeout(function() {
if (++curCat >= cats.length) {
curCat = 0;
}
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
}, 400);
}, 3000);
}
$(document).ready(function() {
catSlider();
});
What is causing this problem? What am I missing?

Timers get throttled back when the tab doesn't have focus (and many other odd games, such as being accelerated when focus returns), so your setInterval and your setTimeout may get out of sync.
Instead, just use setTimeout, where each action (fade out and fade in) triggers the next:
function catSlider() {
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
function fadeOut() {
$(catDisplay).fadeOut();
setTimeout(fadeIn, 400);
}
function fadeIn() {
if (++curCat >= cats.length) {
curCat = 0;
}
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
setTimeout(fadeOut, 3000);
}
setTimeout(fadeOut, 3000);
}
And/or you might consider the callbacks that fadeOut and fadeIn can trigger, in particular on the fadeOut:
function catSlider() {
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
function fadeOut() {
$(catDisplay).fadeOut(fadeIn); // ***
}
function fadeIn() {
if (++curCat >= cats.length) {
curCat = 0;
}
$(catDisplay).html(cats[curCat]);
$(catDisplay).fadeIn();
setTimeout(fadeOut, 3000);
}
setTimeout(fadeOut, 3000);
}
Side note: If you like, you can replace
if (++curCat >= cats.length) {
curCat = 0;
}
with
curCat = (curCat + 1) % cats.length;

The 3000ms interval might run faster than a 400ms timeout as the browser tries to get the interval in sync after you get back to the tab. To resolve this, you could use a delayed loop, e.g.:
const timer = ms => new Promise(res => setTimeout(res, ms));
(async function() {
while(true) {
hideCurrent();
await timer(400);
showNext();
await timer(2600);
}
})()

Related

javascript setTimeout not working inside if statement

I'm writing a code to alert the user inactive when the user is idle for some time.
setIdleTimeout = () => {
var timeout = 0;
timeout = setTimeout(onExpires, 500);
//Expires Function
function onExpires() {
timeout = 0;
alert('Timed out');
}
//This function executes on each mouse move.
function onMouseActivity(event) {
clearTimeout(timeout);
timeout = setTimeout(onExpires, 500);
}
}
The above code works fine. But when ever I add a condition to the clearTimeout, Its not working.
example:
//This function executes on each mouse move.
function onMouseActivity(event) {
var mouseSeconds =5
if(mouseSeconds < 10) {
//Now this is not working.
clearTimeout(timeout);
}
timeout = setTimeout(onExpires, 500);
}
Please help me out. This is driving me crazy for hours now.
Thanks.
you wrote
var mouseSeconds =5
if(mouseSeconds > 10) {
//Now this is not working.
clearTimeout(timeout);
}
mouseSeconds is 5 it will never be bigger then 10 :) so the statement is always false.

Change background images using setTimeout

I have 31 images and I want to display them one after another as the background of a div. I only want it to change when the user hovers over the div. My problem right now is that it just flips through all the images really fast. I am attempting to use setTimeout, but it isn't working. How can I make the delay work?
The name of the div is About_Me_Block and the images are called frame1.gif,frame2.gif ...etc
Here is my code:
function changeImg(counter) {
$('#About_Me_Block').attr("style", "background-image: url(playGif/frame" + counter + ".gif);");
}
$(document).ready(function() {
var hoverAnimate = []
"use strict";
$('#About_Me_Block').mouseenter(function() {
hoverAnimate[0] = true;
var counter = 0;
while (hoverAnimate[0]) {
console.log(counter);
setTimeout(changeImg(counter), 1000);
counter++;
if (counter === 32)
hoverAnimate[0] = false;
}
});
$('#About_Me_Block').mouseleave(function() {
hoverAnimate[0] = false;
$(this).attr("style", "background-image: url(play.jpeg);");
});
});
setTimeout doesn't wait for the function to end, it works lile threading in other languages.
To achieve a what you want, you need to call setTimeout from the changeImg function.
var counter = 0;
$(document).ready(function() {
var hoverAnimate = []
"use strict";
$('#About_Me_Block').mouseenter(function() {
hoverAnimate[0] = true;
counter = 0;
changeImg();
});
$('#About_Me_Block').mouseleave(function() {
hoverAnimate[0] = false;
$(this).attr("style", "background-image: url(play.jpeg);");
});
});
function changeImg() {
$('#About_Me_Block').attr("style", "background-image: url(playGif/frame" + counter + ".gif);");
counter++;
if (counter < 32 && hoverAnimate[0]) {
setTimeout(changeImg, 1000);
} else {
hoverAnimate[0] = false;
}
}
the reason they happen all at once is because while statement doesn't have delay, so all setTimeout will be set up at the same time, thus, calling changeImg all at once.
To solve this problem, you can replace setTimeout with setInterval. Instead of using while, you can just call setInterval like
var counter = 0;
var myTimer = setInterval(changeImg, 1000);
and update counter inside changeImg every time it gets called. After looping, don't forget to
clearInterval(myTimer)
It seems you need to read up on how setTimeout works. It essentially places a reminder to run a function after a given amount of milliseconds have passed. So, when you do setTimeout(changImg(counter), 1000) you are calling changImg(counter) which returns undefined. Therein producing this setTimeout(undefined, 1000) which is why it flips really fast.
So, you can use bind to allow the function to be called later with that parameter built in. Also, make sure you remove the reminders once done with clearTimeout.
function changeImg(counter) {
$('#About_Me_Block').attr("style", "background-image: url(playGif/frame" + counter + ".gif);");
}
$(document).ready(function() {
var hoverAnimate = false, id;
function loop(counter) {
if(hoverAnimate || counter < 32) {
changeImg(counter);
id = setTimeout(loop.bind(this, counter++), 1000);
}
}
$('#About_Me_Block').mouseenter(function() {
hoverAnimate = true;
id = setTimeout(loop.bind(this, 0), 1000);
});
$('#About_Me_Block').mouseleave(function() {
hoverAnimate = false;
// Don't want a reminder for a random counter to wake up.
clearTimeout(id);
$(this).attr("style", "background-image: url(play.jpeg);");
});
});
Two methods for timers - setTimeout and SetInterval (single / repeating)
// setInterval is also in milliseconds
var intervalHandle = setInterval(<yourFuncToChangeImage>,5000);
//this handle loop and make example loop stop
yourelement.yourEvent = function() {
clearInterval(intervalHandle);
};

javascript setTimeout function does not behave normally after 1 call

I have the following function :
function loadInfoBubble(aString)
{
$('#infoBubble').html('<h3>info :</h3><p>'+aString+'</p>');
infoBubble.style.display = "block";
var opacity = 9;
var vanishBlock = null;
var theTimeOut = setTimeout(function()
{
vanishBlock = setInterval(function()
{
if(opacity > 0)
{
opacity--;
}
infoBubble.style.opacity = "0."+opacity;
}, 100);
}, 7000);
var theTimeOut2 = setTimeout(function()
{
infoBubble.style.display = "none";
clearInterval(vanishBlock);
}, 9000);
}
This function is linked to a button by an onclick event.
The function is supposed to display a block which contains a sentence for 9 seconds, and after 7 seconds it starts to vanish.
It behaves normally for the first call, but if I click several times, it doesn't work anymore, even if I let the timeOuts end.
I don't understand why because each timeout or interval belongs to its own variable.
Your code never resets the opacity back to 1. Also, if you trigger the action again before a cycle is finished, the previous cycle isn't canceled. Thus if you trigger the bubble, and then trigger it again 5 seconds later, the first cycle will still run and the bubble will disappear in only 2 seconds. If you click again, the bubble will be faded by the cycle that started on the second click.
I think what you need to do is save the timer references with the bubble object itself, and then reset everything when you want to start a display cycle. You can use the jQuery .data() method for that:
function loadInfoBubble(aString) {
var $bubble = $("#infoBubble");
$bubble
.html('<h3>info :</h3><p>' + aString + '</p>')
.css({ display: "block", opacity: 1 });
var opacity = 9;
var timers = $bubble.data("timers") || {};
clearInterval(timers.vanishBlock);
clearTimeout(timers.showTimer);
clearTimeout(timers.clearTimer);
timers = {
showTimer: setTimeout(function() {
timers.vanishBlock = setInterval(function() {
if (opacity > 0) {
opacity--;
}
$bubble.css({ opacity: "0." + opacity });
}, 100);
}, 7000),
clearTimer: setTimeout(function() {
$bubble.css({ display: "none" });
clearInterval(timers.vanishBlock);
}, 9000)
};
$bubble.data("timers", timers);
}
jsfiddle

Anonymous function in setTimeout not working

I'm trying to create a delay between two loops of the nivo-slider.
Without the setTimeout everything works just fine (but without delay). So the folloing example works:
$('#slider').nivoSlider({
lastSlide: function(){
$('#slider').data('nivo:vars').stop = true;
// setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
// }, 2000);
},
});
If I uncomment the setTimeout-lines the slider stops but does not start again? Any ideas why?
Update:
http://jsfiddle.net/kgYNX/
2nd update:
Tried it with a wrapping function, too. The function gets called but if I use setTimeout in the new function it stops working: http://jsfiddle.net/kgYNX/1/
Solved it slightly different:
beforeChange: function(){
$('#slider').data('nivo:vars').stop = true;
var delay = 0;
if ($('#slider').data('nivo:vars').currentSlide == $('#slider').data('nivo:vars').totalSlides - 2) {
delay = 2000;
}
setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
}, delay);
}
I don't know why "totalSlides - 2", but it works: http://jsfiddle.net/kgYNX/15/
As a variant, you may add custom option to slider vars collection to prevent stop execution on lastSlide handler when slider re-enabled by timeout:
lastSlide: function () {
var dontStop = $('#slider').data('nivo:vars').dontStopOnLast;
if (!dontStop) {
$('#slider').data("nivoslider").stop();
setTimeout(function () {
$('#slider').data("nivoslider").start();
}, 2000);
}
$('#slider').data('nivo:vars').dontStopOnLast = !dontStop;
}

Timer won't die javascript

I am doing some long polling.. and I have a function to make a button blink when a certain statement is true.. Here it is:
function blinking(object, x) {
console.log(x);
if(x>0){
var existing_timer = object.data('clock');
if (existing_timer){
clearInterval(existing_timer);
}
timer = setInterval(blink, 10);
function blink() {
object.fadeOut(400, function() {
object.fadeIn(400);
});
}
}
}
Now.. Notice the timer being set as 'timer'. When someone does something that makes the statement false (making x=0), I want the timer to stop making the button blink when it sees that x=0. This may sound easy but I have tried everything ha.
I've been researching and trying different things, but It doesn't seem to work.
If your variable timer is global, then you should be able to clear the interval using that:
clearInterval(timer);
The integer returned from the setInterval is unique to that timer, so that is what you need to clear.
Here's a simple implementation.
http://jsfiddle.net/AQgBc/
var $led = $('#led'),
blinkState = 0,
blinkTimer,
blinkDuration = 300,
blink = function () {
if (blinkState) {
$led.toggleClass('on');
}
console.log('blink');
blinkTimer = setTimeout(blink, blinkDuration);
};
$('button').on('click', function () {
blinkState = (blinkState) ? 0 : 1;
});
blink();
I just wrote my own without duplicating yours, but the most relevant issue, I think, is just using a setTimeout rather than worrying about a setInterval and clearing that interval.
Edit in response to comment:
If you want it to blink until a user action, then stop polling, it's even simpler.
http://jsfiddle.net/AQgBc/1/
var $led = $('#led'),
blinkState = 1,
blinkTimer,
blinkDuration = 300,
blink = function () {
if (blinkState) {
console.log('blink');
$led.toggleClass('on');
blinkTimer = setTimeout(blink, blinkDuration);
}
};
$('button').on('click', function () {
blinkState = (blinkState) ? 0 : 1;
});
blink();

Categories