javascript setTimeout function does not behave normally after 1 call - javascript

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

Related

setInterval() acts weird after switching tabs

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);
}
})()

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);
};

Only run a JavaScript function if it is not run again after a set amount of time

I have an input which controls the state of an element changing very rapidly. This causes that element to flicker as parts of it change.
I am trying to store these state changes and then providing nothing has changed for a set amount of time (an arbitrary 500ms) change the state.
I have tried to solve this using timeouts as demonstrated in the code below (the same code as in the fiddle.):
var changingToHappy = false;
// Original no attempts to fix functions.
//var ifHappy = function () {
// $("#face").text(':)');
//};
//
//var ifNotHappy = function () {
// $("#face").text(':(');
//};
var ifHappy = function () {
changingToHappy = true;
setTimeout(function () {
if (changingToHappy) {
$("#face").text(':)');
}
}, 500);
};
var ifNotHappy = function () {
changingToHappy = false;
setTimeout(function () {
if (!changingToHappy) {
$("#face").text(':(');
}
}, 500);
};
$("#textBox").keypress(
function (event) {
if (event.which == 49) {
ifHappy();
$("#flickerFace").text(':)');
}
if (event.which == 50) {
ifNotHappy();
$("#flickerFace").text(':(');
}
}
);
If you rapidly press 1, 2, 1, 2 and so on in the fiddle the face will remain not flickery for a moment and then the timeouts will catchup and it will begin to change state.
This fiddle http://jsfiddle.net/9w70wxgz/4/ simulates the problem.
To clarify I only want the face to change if nothing has tried to change its state for a set amount of time.
What you're looking for is called a debounced function, here is an example with a piece of your code (you're almost there):
//storage for timer
var notHappyTimer;
var ifNotHappy = function () {
changingToHappy = false;
//removes timer if event fires in less than 500ms
clearTimeout(notHappyTimer);
//resets it to attempt again in 500ms
notHappyTimer = setTimeout(function () {
if (!changingToHappy) {
$("#face").text(':(');
}
}, 500);
};
As you can see, you just assign the timeout to a variable that clears itself every time the function is fired, then starts the timer again. This ensures that the text change only happens if the function hasn't been fired in 500ms.

jQuery increment variable in recursive function

There are other questions with almost the same title as this but they don't seem to answer my question.
I am trying to make a simple javascript function that uses jQuery to fade div blocks in and out one after the other, and goes back to the first div once it get's to the end. This should continue while the user is on the page, much like a slideshow.
There are 5 'slides' or divs to be shown and hidden in sequence. They have the classes 'content1', 'content2', 'content3' etc.
$(document).ready(function() {
var slide = 1;
nextSlide(slide);
function nextSlide(slide) {
$('.content' + slide.toString()).fadeTo(2000, 1.0).delay(5000).fadeTo(2000, 0.0).delay(2000);
if(slide > 5) {
slide = 1;
} else {
slide++;
}
nextSlide(slide);
};
});
This does not fade each div in and out in sequence, what it actually does is fade all of the slides in and out simultaneously.
How do I get it to reference each slides class in turn and then loop back to the first slide?
Many thanks.
Your function will recurse immediately, dispatching all of the animation requests around the same time.
To stagger them, you should recurse with a timeout:
$(document).ready(function() {
var slide = 1;
nextSlide();
function nextSlide() {
$('.content' + slide.toString()).fadeTo(2000, 1.0).delay(5000).fadeTo(2000, 0.0).delay(2000);
if(slide > 5) {
slide = 1;
} else {
slide++;
}
setTimeout(nextSlide, 2000); // second param is delay in ms
};
});
This will cause the next call to occur after 2000ms. Thanks to closure, your slide variable should be captured and will persist its value between calls.
No need for a timeout. Simply call the next iteration as a callback of the last fadeTo method:
$(document).ready(function() {
var slide = 1;
nextSlide(slide);
function nextSlide(slide) {
$('.content' + slide.toString())
.fadeTo(2000, 1.0)
.delay(5000)
.fadeTo(2000, 0.0, function(){
if(slide > 5) {
slide = 1;
} else {
slide++;
}
nextSlide(slide);
});
};
});
http://jsfiddle.net/3L09b505/

onmouseover stop slideshow help

This is a function for a slideshow,onmouseover i want it to stop. Instead of stopping the slideshow onmouseover, it speeds up?? How can i correct this to stop onmouseover?
<body onload="nextslide();">
function nextslide() {
// Hide current slide
var object = document.getElementById('slide' + current); //e.g. slide1
object.style.display = 'none';
// Show next slide, if last, loop back to front
if (current == last) {
current = 1;
} else {
current++;
}
object = document.getElementById('slide' + current);
object.style.display = 'block';
var timeout = setTimeout(nextslide, 2500);
object.onmouseover = function(){
clearTimeout( timeout );
}
object.onmouseout = nextslide;
}
I tried your code and the only problem I can see is on "onmouseout" there is an immediate transition to next slide. I would change that line like this:
object.onmouseout = function() {
timeout = setTimeout(nextslide, 2500);
}
I disagree with Jared, "timeout" is defined there because you are using nested functions and inner functions have access to outer functions scope. You should never omit var when defining variables.

Categories