Fully Executing jQuery Animations Without Queuing

Avatar of Chris Coyier
Chris Coyier on (Updated on )

It is pretty common when using jQuery’s .animate() function that it is triggered by a mouseEnter or hover event. That’s all well and good, but it means that we need account for those events being triggered multiple times. If the element with the hover event attached is hovered over multiple times, that means the animation will be fired of several times, which is typically undesirable. The standard way to deal with this is using the .stop() function, like:

$(this).stop().animate({ width: "200px" });

This is definitely not the cure-all solution though. So let’s explore.

We already know that not using .stop() is problematic, because then the animations queue up and it is kind of awkward with multiple quick hovers. But not using .stop() is also kind of perfect in a way, because the mouseEnter animation and mouseLeave animation execute completely and sequentially. It is that smoothness that I am after here, only without the queuing.

Using .stop() prevents the queuing, but it also prevents the animations from completing a full cycle. Mouse off, the stop function fires, and stops the animation that triggered on mouseEnter and begins the animation resetting things.

The first thing I started messing with was in using .stop() prior to only one or the other of the animations, but that doesn’t help much. The .stop() function also has some parameters you can pass, the second of which dictates if the animation should be forced to complete first. If set to true, the animation will indeed finish, but it doesn’t do so smoothly, it jerks to it’s final state, which I find generally undesirable.

The solution lies in not beginning a new animation until the state of the element is not being animated. In that way, you don’t even need to worry about queuing, because only one animation can be running at a time anyway. There are a couple of ways to get there, but this I found the cleanest:

$("div").hover(function(){
    $(this).filter(':not(:animated)').animate({ width: "200px" });
}, function() {
    $(this).animate({ width: "100px" });
});

There was plenty of ideas on the journey here. Check out the demo below to see all the different options I went through, as well as an additional method that also works.

View Demo

UPDATE: Definitely check out Ralf Stoltze’s even better solution.