Fixing Javascript animation timings in inactive tabs with real time fix - javascript

So I have a fiddle with two pulsing animations running at different times.
http://jsfiddle.net/JuFxn/16/
The code for the pulse is here. There is more in the fiddle so please check it out.
function fadeItIn() {
var child;
child = 4;
setTimeout(fadeIn, 3000);
function fadeIn() {
$("#child" + child).fadeIn(175);
--child;
if (child >= 0) {
// Continue fading in
setTimeout(fadeIn, 175);
} else {
// Start fading out
++child;
setTimeout(fadeOut, 175);
}
}
function fadeOut() {
$("#child" + child).fadeOut(175);
++child;
if (child <= 4) {
// Continue fading out
setTimeout(fadeOut, 175);
} else {
// Start over again
setTimeout(fadeIn, 3000 - 1575);
}
}
}
The issue I'm having is that when the tab becomes inactive, the timings of the two pulses desync and go off pretty far from each other. I did some research and found this
How can I make setInterval also work when a tab is inactive in Chrome?
They seem to fix the problem by implementing a real world counter in and adding the value of the counter during each cycle and having the div move based on the distance generated by the counter. Would implementing something like that fix the problem I am having? How would I even use it? I think the problem is arising from the use of the setTimeout on the second function.
setTimeout(fadeItInDoom, 500);
If I take out the setTimeout and make it so the two pulses execute at the same time, the timings never go off.

So I ended up figuring out a fix to make it stay in sync.
http://jsfiddle.net/bwCmk/
I changed the animation code entirely so that it is running in jQuery. The fix however comes in how I dealt with changing to the next animation. I just made a different function for each pulsing element and had the next one called at the end of the previous function. So:
FunctionA {
code
functionB();
}
FunctionB {
code
functionC();
}
etc
it worked. Just putting this up so that if anyone else makes something along these lines, they can find a fix for it. Thanks to everyone who answered.

Related

capture setTimeout timerID when setTimeout is used in an if loop

I'm using setTimeout to create a pause in a loop (on mouseenter) and need to capture the timer ID so it can be stopped on mouseleave.
I thought that would be a simple task… before I tried to code it.
I have a gallery of thumbnails and on hovering over a thumbnail I want to cycle though a series of images (in an endless loop); kind of like a mini-carousel. I achieve this with the common method of swapping out the thumbnail’s source file path; I capture and store the thumbnail’s file path, loop through an array of images then replace the original thumbnail on mouseleave. Nothing complicated.
I coded this up and everything worked fine except on mouseenter the function looped through the entire array of images no matter how briefly the mouse hovered. I eventually discovered that you can’t actually pause a function or stop it but by wrapping it in a setTimeout and using a boolean flag you can create that effect. This is the answer I found https://stackoverflow.com/a/19192399 by user1693593, it’s really simple and works really well.
var doLoop = false;
function loopy() {
if (doLoop === true) {
setTimeout(function () {
// code to loop through array of images
loopy();
}, 11);
}
};
I'm using jQuery's hover method to set the flag.
$(".thumbnails").hover(function() {
doLoop = true;
}, function() {
doLoop = false;
});
So I coded that up and it worked perfectly when you hover over the first thumbnail but when you move to a new thumbnail it all goes haywire. I figured out this was because the first timeout wasn’t being cancelled and subsequent timeouts were interfering with it (and each other). I can see from various Stack Overflow questions that this is a common problem. I discovered that setTimeout returns an ID that you need to capture if you want to cancel it with clearTimeout.
So I found an example on MDN setInterval whereby you store the timeout ID in a variable. It looked simple enough so I coded that up and it worked perfectly. I can see in the console all the timeouts being set (with unique IDs) and cancelled as the mouse moves over the thumbnails… but now I can’t get the loop to work.
I can’t understand why this is so complicated in JavaScript, surely something like this is a common requirement.
Can someone please take pity on a relative newbie and explain how I can code user1693593's example so that it works when mousing over multiple elements. I don’t mind if it uses setTimeout or setInterval.
Please don’t mark this as a duplicate – I’ll understand if someone does – but I’ve been all over Stack Overflow (plus MDN and W3Schools) and I can’t find an example that specifically answers this question, certainly not one that I can understand anyway.
Store the reference somewhere and cancel it.
var timer = null
function addOne (elem) {
elem.textContent = (+elem.textContent + .1).toFixed(1)
}
$(".foo")
.on("mouseenter", function () {
var elem = this
timer = window.setInterval(function () { addOne(elem) }, 100)
}).on("mouseleave", function () {
if (timer) window.clearInterval(timer)
})
div.foo {
width:100px;
line-height: 100px;
border: 1px solid black;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>
Just keep track of the element that’s currently doing the loop instead of a boolean true/false.
var loopEl;
function loopy() {
if (loopEl) {
setTimeout(function () {
// code to loop through array of images
// do something with loopEl
loopy();
}, 11);
}
};
$(".thumbnails").hover(function() {
loopEl = this;
}, function() {
loopEl = false;
});

Image flickering with setInterval() function

I have made a carousel and using JavaScript setInterval() function for rotate image with fixed interval in carousel. Here's the script that I had used:
var timeOut = 4000;
function showSlide() {
//....script for showing image
}
function pauseSlide() {
setInterval(function(){showSlide();}, timeOut);
}
jQuery(document).ready(function() {
pauseSlide();
});
Now the problem is when I have change the browser tab and after few minute back again to carousel browser and what I seen carousel running too faster rather than default time interval, images going to change fast suppose 0 time interval. Please help me with how I can sort this out.
You must get rid of the first interval before starting another, or you start getting more than one interval working simultaneously (i.e. why you start seeing it go "faster")
Do this
var timeOut = 4000;
var interval = 0;
function showSlide() {
//....script for showing image
}
function pauseSlide() {
clearInterval(interval);
interval = setInterval(function(){showSlide();}, timeOut);
}
jQuery(document).ready(function() {
//NOW you can do multiple pauseSlide() calls
pauseSlide();
pauseSlide();
pauseSlide();
pauseSlide();
pauseSlide();
});
From what I know in newer versions of both firefox and chrome, background tabs have setTimeout and setInterval clamped to 1000ms to improve performance. So I think that your issue might relate to that.
Maybe this will help : How can I make setInterval also work when a tab is inactive in Chrome?
Image changing faster than expected may indicate that you have more than one call to pauseSlide(), in one way or another.
Is document ready the only place you call the function ? Any code in showslide or anywhere triggering a document ready event ? If you put an alert() in pauseSlide(), does it popup more than once ?

Jquery Mouse Over Movement conflicting with other animate function

im currently working on my first website- but ive come to a slight problem with this piece of jquery... The page is:
http://beelinetest.site50.net/the_arts_and_culture_in_worcester.html
I have a rollover over effect throughout the completed pages on my website, (when you hover over the link, the word slightly moves to the right) this can be better seen on the events programme page.
But on this page ive got a lot of jquery going on... When clicked the word moves to the left off the page. But because of having the mouse over, (moving the word back to the right on release) the word shoots back on screen to its original place.
Please feel free to look into my code, id really appreciate it- i know you guys are great, so thanks in advance! Tom
Here are the two conflicting codes-
$("#exploretext").click(function(){
$(".moveeverythingleft").animate({"left":"-220px"}, 400);
return false;
});
$("#exploretext").hover(function(){
$("#exploretext").animate({"left":"227px"}, 50);
}, function(){
$("#exploretext").animate({"left":"217px"}, 150);
});
This will unbind your hover effect after a click event :
$("#exploretext").on("click",function()
{
$(this).off("hover", "**");
$(".moveeverythingleft").animate({"left":"-220px"}, 400); return false;
});
$("#exploretext").on('hover',function()
{
$("#exploretext").animate({"left":"227px"}, 50); }, function()
{
$("#exploretext").animate({"left":"217px"}, 150);
});
});
You may have to rebind it later, or to bind another, it depends on what you want to achieve.
It may be way easier to use another animate function, using += :
$("#exploretext").animate({"left":"+=10px"}, 50, 'linear');
edit :
I'm reading your code, the markup and the css is a bit of a mess, I think it makes your work harder than it should be.
I would advise you to try first to clean the HTML and the CSS, then to handle the JS part.
what u can do is store the state of the button in some variable like ..
var position = "LEFT OR RIGHT";
then when u send the button to the left change the variable to left.
and in the hover event check the value and then if the position is left then dont execute whole of the event and if the position is right then execute the event.
function OnExploreClick(){
position = 'left';
//ur code
}
function OnUnExploreClick()
{
//put the button to right side// ur code
then position = 'right';
}
function OnHover()
{
if (position == 'right')
{
//execute ur code
}
}

setTimeout speeds up with multiple tabs

I’m having a setTimeout problem similar to this one. But that solution doesn't help me since I can’t use php in my file.
My site has a slider with a list of images that move every 8 seconds.However, when I have opened a few tabs in the browser and then switch back again, it goes nuts.
The slider proceeds to move the images one after the other immediately without the 8 second timedelay.
I'm only seeing it in Chrome and the latest Firefox.
**EDIT: I checked with console.log() and the setTimeout returns the same number before and after the clearTimeout. Not sure why. Maybe that also has something to do with it? **
EDIT 2: I added a fiddle: http://jsfiddle.net/Rembrand/qHGAq/8/
The code looks something like:
spotlight: {
i: 0,
timeOutSpotlight: null,
init: function()
{
$('#spotlight .controls a').click(function(e) {
// do stuff here to count and move images
// Don't follow the link
e.preventDefault();
// Clear timeout
clearTimeout(spotlight.timeOutSpotlight);
// Some stuff here to calculate next item
// Call next spotlight in 8 seconds
spotlight.timeOutSpotlight = setTimeout(function () {
spotlight.animate(spotlight.i);
}, 8000);
});
// Select first item
$('#spotlight .controls a.next:first').trigger('click');
},
animate: function(i)
{
$('#spotlight .controls li:eq(' + (spotlight.i) + ') a.next').trigger('click');
}
}
From the jQuery documentation:
Because of the nature of requestAnimationFrame(), you should never
queue animations using a setInterval or setTimeout loop. In order to
preserve CPU resources, browsers that support requestAnimationFrame
will not update animations when the window/tab is not displayed. If
you continue to queue animations via setInterval or setTimeout while
animation is paused, all of the queued animations will begin playing
when the window/tab regains focus. To avoid this potential problem,
use the callback of your last animation in the loop, or append a
function to the elements .queue() to set the timeout to start the next
animation.
I finally found my answer and it’s not at all what I was expecting.
It seems the culprit is jQuery’s .animate(), which I use to move the images in the slider.
I calculate and move my images positions with this:
$('.spotlight-inner')
.animate(
{ left: scrollToVal },
{duration: 'slow'}
)
;
Now the problem seems to be that in some browsers, after you switch to a new tab and back, jQuery’s .animate() saves up the animations and fires them all at once. So I added a filter to prevent queueing. That solutions comes from CSS-Tricks.com :
$('.spotlight-inner')
.filter(':not(:animated)')
.animate(
{ left: scrollToVal },
{duration: 'slow'}
)
;
The first slide you see when you go back can act a little jumpy but it’s better than the superspeed carousel from before.
Fiddle with the full code here
There is an easier way using the jquery animate queue property:
$(this).animate({
left: '+=100'
}, {duration:500, queue:false});
I don't know if this will help you, but it helped me with my slideshow. What I did was everytime I called an animation that was supposed to happen at a set interval because of the setTimeout, I called clearQueue() which would get rid of any other animations that had been set to happen. then i'd call the animation. That way when you come back to that tab, you don't have all these animations queued up and it goes crazy. at max you'll only have one set up.
So something like this:
spotlight.timeOutSpotlight = setTimeout(function () {
spotlight.clearQueue(); // get rid of other instances of the animation
spotlight.animate(spotlight.i);
}, 8000);
It may not work in all cases (depending on timing), but I hope that helps somebody!
You must also think you use clearTimeout.
As you call setTimeout function it returns an ID you can save this ID in a variable like
timeoutID = setTimeout(function () {
spotlight.animate(spotlight.i);
}, 8000);
and before setting a new timeout you can call the function like
clearTimeout(timeoutID)
My suspicion is that the browser queues input events like 'click' but only fires them when the tab where the event occurs actually has focus.
Perhaps you should try calling your click callbacks directly instead of using trigger('click').
Something like this:
spotlight: {
i: 0,
timeOutSpotlight: null,
clickFunc: function(element) {
// do stuff here to count and move images
// Clear timeout
clearTimeout(spotlight.timeOutSpotlight);
// Some stuff here to calculate next item
// Call next spotlight in 8 seconds
spotlight.timeOutSpotlight = setTimeout(function () {
spotlight.animate(spotlight.i);
}, 8000);
},
init: function()
{
$('#spotlight .controls a').click(function (e) {
// Don't follow the link
e.preventDefault();
spotlight.clickFunc(this);
});
// Select first item
spotlight.clickFunc($('#spotlight .controls a.next:first'));
},
animate: function(i)
{
var element = $('#spotlight .controls li:eq('+spotlight.i+') a.next');
spotlight.clickFunc(element);
}
}
What version of jQuery are you running? Apparently this problem was 'fixed' for version 1.6.3 - they reverted the change that caused this to happen. Discussions here and here.
Though this issue will likely have to be addressed in the future, it seems as though we're off the hook for now.

Can't see problem in JS

I want that when mouse is over an image, an event should be triggered ONCE, and it should be triggered again only after mouse is out of that image and back again, and also at least 2 seconds passed.
If I leave my mouse over the image,it gets called like every milisecond,and by the logic of my function once you hover on the variable 'canhover' becomes 0 until you move mouse out
This code seems to have a bug and I cant see it. I need a new pair of eyes, but the algorithm is kinda logical
Working code :
<script type="text/javascript">
var timeok = 1;
function redotimeok() {
timeok = 1;
}
//
function onmenter()
{
if (timeok == 1)
{
enter();
timeok = 0;
}
}
//
function onmleave()
{
setTimeout(redotimeok, 2000);
leave();
}
//
$('#cashrefresh').hover(onmenter,onmleave);
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
}
</script>
I don't know if this will solve your entire problem (since we don't have a detailed description of what it is), but instead of:
$('#cashrefresh').hover(onmenter(),onmleave());
try:
$('#cashrefresh').hover(onmenter,onmleave);
And the same thing here:
setTimeout(redotimeok, 2000); // just the function name
Also, I don't see where you ever set timeok to zero. Do you mean to set timeok = 0 in onmenter()?
There are two methods in jquery for your problem:
.mouseenter() and .mouseleave()
Check out the demos there.
EDIT:
I thought hover was for mouseover and mouseout, sorry for confusion.
I checked your code again. And it seems that you're changing the image when mouse gets over the image, which forces browser to load the new image and the old image disappears for a very little while till the new one appears and i think this must be triggering both handlers continuosly and you're getting this behaviour.
Try not to change the source of the image, comment out that line and instead console.log("some message") there and see if the message is repeated as much as .load() was fired before.
Hope this helps.
Try changing onmleave function as follows:
function onmleave()
{
setTimeout(redotimeok, 2000);
leave();
}

Categories