Prevent broken animations in jQuery

Note: This article has been marked for a quality review and will soon be updated.

I recently posted an article on Animating a Site’s Loading using jQuery – a handy technique to spice up any site. But an issue that became apparent using this technique, along with some other animation methods, is that sometimes user interaction can prevent loading and other animations from finishing correctly, and possibly lead to pages that look broken because of it. This is the sort of thing that only user-testing will usually uncover, as many web-designers simply sit back and watch the animations, before actually interacting with the site. To explain what I mean I’ll show you a basic example of navigation animations – and the one that prompted this article.

$('nav li').each(function(index){
    $(this).slideDown(200*(index+1));
});

$('nav li').hover(function(){
    $(this).stop().animate({'paddingTop':'10px'},200);
});

Now the code above works perfectly fine – it slides-down each list element in my navigation panel, and whenever a user rolls over one with their mouse the list element moves down by 10 pixels. Great, but we have a slight problem; namely, the stop() function. The code uses it to prevent an animation build up if a user quickly moves their mouse over multiple list elements for example, and for that it works very well. But because we are using a loading animation, if a user hovers over a list element before it has completed the the slide-down animation, the list element will be stuck in limbo, or rather, it won’t finish sliding down.

This is caused by the fact that the hover() is bound to each list element at the same time that the animations are being executed. To solve this issue we need to come up with a way of waiting for the animations to complete before binding any events to our elements. One way of doing this might be to add a class to each element as it finishes it’s loading, and then checking to see if the class exists before performing any events. But that approach will quickly clog up both your JavaScript, and HTML. To avoid messy code, we should instead only bind such events once the animation has completed.

To do that, we just make a simple alteration, like so:

$('nav li').each(function(index){
    $(this).slideDown(200*(index+1), function(){
        $(this).hover(function(){
            $(this).stop().animate({'paddingTop':'10px'},200);
        });
    });
});

The code above uses the callback function of the slideDown() function to wait until the animation completes. We then bind the hover event to each element individually, and we have ourselves a much better setup to prevent our sites breaking in the hands of hover-happy users.


Posted

in

by