Grow your CSS skills. Land your dream job.

Fully Executing jQuery Animations Without Queuing

Published by Chris Coyier

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.

Comments

  1. FJ
    Permalink to comment#

    Demo page is not working. I saw in the source-code :-/

  2. Wow..!

    That’s great !

    Thanks man..!

  3. Wow, very clever. Thanks! I always relied in the .stop() but never liked the sudden cut in the animation.

  4. Oh, I just tested the demo page. And with the new method you propose, the animations are triggered on all elemnts when they are hovered fast. I’m not sure if I’m making sense. But for example, if you use this for a menu, and the user just pass the mouse quickly through all the menu, everything will trigger at the same time (which didn’t happend with stop()).

  5. Permalink to comment#

    Very interesting, Chris.

    I could think of instances where one method would be preferred over another (running full cycles vs. not).

    Insightful. Thanks for this.

  6. Permalink to comment#

    Cool! Great tips for future projects.

  7. Permalink to comment#

    Great exploration — this is a problem I’ve been playing around with a lot.

    If this was a menu, there’s still a major problem though. The problem with the filter is that if you move your mouse outside the element and then enter it again before it animates back to it’s default state, it won’t animate to it’s hover state again — even though you are hovering it.

    Super confusing. I actually prefer the .stop() solution — it’s not the prettiest, but it actually works.

    • bill
      Permalink to comment#

      This is exactly right. At first I thought Chris’ solution was better than using stop(), but you hit the nail on the head. I’ll stick with stop() for now.

    • This is definitely a good point, for something like a dropdown menu where the hover animation does something other than something purely aesthetic.

  8. I just tested out the demo and it seems that the “Dequeue” version is the only one that worked reliably. The “Animated Test” would do a “cycle” once, then I would go away and hover it again (after animation was obviously done) and nothing would happen.

  9. Very cool tutorial and illustration. These can be very useful for many circumstances.

  10. Very cool! I now using:

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

    It’s good?

  11. Niall
    Permalink to comment#

    Cool examples, they all seem to have their flaws though. I’d just prefer queueing.

  12. Permalink to comment#

    Chris, I am in awe of your jQuery knowledge. This is a fantastic set of examples! Thank you for your insight and the demos!

  13. Permalink to comment#

    Chris you should adopt Duke Nukems’ sayings… namely in this instance

    “damn i’m good”.

  14. Skare
    Permalink to comment#

    Those two bottom examples on demo pages… When you trigger the mouseover-event, then quickly mouseout and go back mouseover there’s troubles.

    The animation goes back to non-active-state and the mouse cursor is on top of the link and nothing happens.

  15. Using the :not(:animated) for a long time now. And it’s the best. Sometime i use queue:false but not much, and i almost never use the .stop().

  16. Permalink to comment#

    Excellent jQuery again Chris. Tnx. (:

  17. Permalink to comment#

    Wow, beautiful solution, thanks Chris.

  18. Permalink to comment#

    Very nice! I could actually use this immediately in two places at my web site. =)

  19. Permalink to comment#

    I know that suggesting a plug-in doesn’t really help for people interested in learning programmatic solutions to problems they might be having (well, unless they’re keen to dig into the plug-in’s source code) but I’ve had good success with hoverintent.

    The default behaviour isn’t always ideal for everything (also depends on how unrealistic you want to make your tests), but I’ve found that with a little tweaking it’s going to do a good job of detecting…er… your intent… to hover!

  20. Permalink to comment#

    *Sorry OOT*
    Chris, what kind Related Post plugin that you currently used on this blogs?

  21. My hoverFlow plugin is another solution to animation queue buildup. It also honors a single mouseover/-out with a full animation cycle.

    http://www.2meter3.de/code/hoverFlow/

    It’s nice to see that you also went the long way down (… the demo page) to find a good solution.

    hoverFlow also solves the menu problem mentioned in the comments (“quickly mouseout and go back”). See the submenu examples.

    I found that both the :animated pseudo-selector and the save-state-in-classname solutions are too slow (especially in older IEs). My approach is to save state using jQuery’s data()-function.

    The actual animation is triggered inside an anonymous function that is queue()d. That way, I can control animation queueing based on the current state (mouse position).

    • NICE. This is really slick Ralf.

    • Permalink to comment#

      This plugin is great. I’m trying to find a use for it on my site now. Really looks nice.

    • LuK
      Permalink to comment#

      I use hoverflow for a long time =)…hate the .stop(), doesn’t work for me at all, looks just ugly^^…hoverflow is a real beauty! But thanks for pointing the problem out Chris!

  22. Giovanni
    Permalink to comment#

    Great work here! I had a similar problem to solve with an animation in Flash using actionscript. Found a solution by using the timeline to play the animation backwards from the point of mousing off, and play forward on the hover event.

    Even your first solution however works great on a mobile device (where it wouldn’t using flash, but still hoping) because You can’t hover with a mobile (least not on the iPhone, I’m using it as I write this).

    Any ideas about how to code a menu item with a pop-down with a hover state for a non-mobile user with it working also for a mobile user? I’ve had this problem on sites I’ve been browsing and I’ve had to click then quickly stop loading the page to access the hover menu items….

  23. Diegop
    Permalink to comment#

    Very nice Chris, i customize your filter code a little
    Add .stop() before .filter()
    works nice!
    $(“#animate-test div”).hover(function(){
    $(this).stop().filter(‘:not(:animated)’).animate({ width: “200px” });
    }, function() {
    $(this).animate({ width: “100px” });
    });

  24. Permalink to comment#

    This is very nicely done! I love how there are many samples on the demo page.

  25. Permalink to comment#

    god, you helped me so much with this..
    thank you! :)

  26. Permalink to comment#

    Is there a similar solution for click event?

    • Permalink to comment#

      Yah i tried on a click event, its worked great for me, the event doesn’t matter, more so the animate is what this fixes.

  27. Permalink to comment#

    Chris you are freeeekin awesome!!!

    it helped me with this! :–>
    hookah falling test…

  28. Kappi Craig
    Permalink to comment#

    Chris, as ever your guides and tuts are superb. Can’t thank you enough.

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".