Blog

Getting around jQuery animation bugs in Firefox and Chrome

At Newedge, we recently had a site to build where the client required a vertical news ticker. We immediately thought ‘Simple. We’ll use jQuery’. With its vast library of built in and community built modules we were sure that we could find something, or at the very worst, be able to use some of jQuery’s animation features to build our own.  Fairly quickly we came across vTicker, a plugin that would do what we were after.

After a few modifications, mainly to change the tick speed and the speed of the animation when shuffling articles, we slotted vTicker into the site and began to test.

Everything seemed to be going well and vTicker looked perfect. Unfortunately, when we passed the site over to a non-technical member of staff to look over we discovered a problem. The person we had asked to test the site had been looking through it and switched tabs in his browser and left our new site in the background for a few minutes. On returning to the first tab, we found all of our animations on the page playing catch up and the vTicker repeating the same article over and over again, at very high speed.

During our investigation, we found 2 separate issues coming from the same source. jQuery animations. More specifically jQuery animations and Firefox/Chrome.

When jQuery handles an animation, it adds the animation to a queue and runs it as soon as the previous animation has finished. This works great, until you add in Firefox and Chrome trying to help by not rendering any animations if the window or tab containing the animations is not focused. Here was the source of our animations racing to catch up with where they thought they should be in the queue.

To solve this, we need to use jQuery’s .stop() function before we run the next animation. The .stop() function will stop the current animation being run on selected page elements and takes 2 optional arguments. The first being ‘clearQueue’ which will clear the rest of the current queue and the second being ‘jumpToEnd’ which will jump to the end of the current animation.

For our issue, we only needed to clear the animation queue with .stop(true):

current.stop(true).animate({opacity: 0.0}, 1000);

As you can see, we stop the animation on the selected element and then instruct jQuery to animate it again. This means that regardless of the number of animations that have been queued up, we will ignore them and move straight onto the next animation that the browser is expecting, which prevents our animations from playing catch up.

Our second problem, that if the vTicker repeating articles, was a little more interesting. To make it easier to understand, we need to delve a little bit into how vTicker shows the news articles and rotates them.

When we create a vTicker object on a page, we create a div for each article that we want to cycle through. So if you have 16 articles to show, but only want 3 to be visible at any time, you still create 16 divs, but tell vTicker to only show 3 at a time. What vTicker will then do, is to replicate the top most div, place this clone at the bottom of the collection of divs, and then delete the top most div to make room for the next article. Before deleting the div, vTicker will add a rather nice fading up animation to the top div so that we don’t just have divs blinking in and out of existence.  What we found was that, because we queuing the .animate() on the div that we wanted to delete, we were also queuing the command to delete the old div. This meant that we were cloning the top most div over and over again, while the window is unfocused but not deleting the original. The end result was that when returning to the page we were clearing the animation queue with .stop(true), which also contained the command to remove the original div.

The solution that we finally settled upon was to tell jQuery to switch off animations when the window is unfocused and switch them back on when the window becomes focused again using jQuery.fx.off:

$(window).focus(function(){jQuery.fx.off = false;}); $(window).blur(function(){jQuery.fx.off = true;});

This means that the page will still clone the top most div but, importantly, it will still delete the div after it clones it, before it tries to clone again. This works because we don’t have any animations to queue, so we don’t queue the delete commands.

Comments are closed.