25

Question

The solution below is intended to slide down the groupDiv displaying div1 and enough space for div2 to slide in. It's all achieved by chaining the animations on the #Link.Click() element.

It seems to bug out, though, when the link is clicked rapidly. Is there a way to prevent this? By perhaps disabling the Click function until the chained animations are complete? I currently have checks in place, but they don't seem to be doing the job :(

Here's the code i'm using:

Custom animate functions.


//Slide up or down and fade in or out
jQuery.fn.fadeThenSlideToggle = function(speed, easing, callback) {
    if (this.is(":hidden")) {
        visibilityCheck("show", counter--);
        return this.slideDown({duration: 500, easing: "easeInOutCirc"}).animate({opacity: 1},700, "easeInOutCirc", callback);
    } else {
        visibilityCheck("hide", counter++);
        return this.fadeTo(450, 0, "easeInOutCirc").slideUp({duration: 500, easing: "easeInOutCirc", complete: callback});
    }
};

//Slide off page, or into overflow so it appears hidden.
jQuery.fn.slideLeftToggle = function(speed, easing, callback) {
    if (this.css('marginLeft') == "-595px") {
        return this.animate({marginLeft: "0"}, speed, easing, callback);
    } else {
        return this.animate({marginLeft: "-595px"}, speed, easing, callback);
    }
};

In the dom ready, i have this:


$('#Link').toggle(
    function() {
        if (!$("#div2 .tab").is(':animated')) {
            $("#GroupDiv").fadeThenSlideToggle(700, "easeInOutCirc", function() {$('#div2 .tab').slideLeftToggle();});
        }
    },
    function(){
        if (!$("#groupDiv").is(':animated')) {
            $('#div2 .tab').slideLeftToggle(function() {$("#groupDiv").fadeThenSlideToggle(700, "easeInOutCirc", callback);} );
        }
    }
);

HTML structure is this:


<div id="groupDiv">
     <div id="div1">
          <div class="tab"></div>
     </div>
     <div id="div2">
          <div class="tab"></div>
     </div>
</div>
Scotty
  • 2,545
  • 6
  • 27
  • 39

6 Answers6

23

The issue is your first animating the div#GroupDiv so your initial check if (!$("#div2 .tab").is(':animated')) will be false until the groupDiv has finished animated and the callback is fired.

You could maybe try

if (!$("#div2 .tab").is(':animated') && !$("#GroupDiv").is(':animated')) 

however I doubt this will cover really quick clicking. The safest is to unbind the event using

$(this).unbind('toggle').unbind('click');

as the first line inside the if and you can then do away with the animated check. The downside to this is you will have to rebind using the callback you are passing through to your custom animation functions.

redsquare
  • 75,782
  • 18
  • 147
  • 156
  • Hmm yeah that could work. Are there any serious down sides to this? I'll give it a go now and get back to you. – Scotty Aug 13 '09 at 13:29
  • apart from the perf overhead of unbind/binding events not much else. – redsquare Aug 13 '09 at 13:34
  • I removed the if statements from the toggle functions, and added in $(this).unbind('toggle'); However, it doesn't seem to be unbinding the toggle whatsoever. :S – Scotty Aug 13 '09 at 14:08
  • 1
    try $(this).unbind('toggle').unbind('click'); – redsquare Aug 13 '09 at 14:12
  • Works a dream! That's the unbinding part sorted. Although, binding again in the callback is posing another problem. $("groupDiv").fadeThenSlideToggle(700, "easeInOutCirc", function() {$('#div2 .tab').slideLeftToggle(700, "easeInOutCirc", function() { $(this).bind('toggle').bind('click');} );}); Throws back a crazy error stemming from the jquery.js file: TypeError: Result of expression 'H' [undefined] is not an object. So i'm assuming something isn't being passed properly through the callback? – Scotty Aug 13 '09 at 15:23
  • my problem was not the same but this answer worked for me – thedudecodes Oct 04 '16 at 11:41
18

You can easily disable your links while animation is running

$('a').click(function () {
    if ($(':animated').length) {
        return false;
    }
});

You can of course replace the $('a') selector to match only some of the links.

RaYell
  • 66,181
  • 20
  • 123
  • 149
  • That is what he is doing essentially – redsquare Aug 13 '09 at 13:17
  • 2
    I like `$('a').click(function () { if ($(this).is(':animated')) { return false; } });` a little better. – GFoley83 Mar 15 '13 at 05:40
  • 1
    @GFoley83 your code will work only if link is animated. My version checks if anything in the DOM is animated. – RaYell Mar 15 '13 at 09:53
  • @RaYell Yep that's correct. Depending on what you're trying to accomplish, checking inside the current scope should be all that's needed E.g. stopping multiple clicks to show and hide a mobile nav. ":animated" isn't part of the css spec, it's a jQuery extension. So running it without a filter, especially over the entire DOM (which is what you recommended) is very inefficient. – GFoley83 Mar 15 '13 at 20:27
3

Animating something that can be clicked repeatedly is something to look out for because it is prone for errors. I take it that you Problem is that animations queue up and are executed even when you have stopped clicking. The way I solved it was to use the stop() function on an Element.

Syntax: jQuery(selector).stop(clearQueue,gotoEnd) //both parameters are boolean
More Info

When I click on a button, I first stop the animation and clear the Queue, then i proceed to define the new animation on it. gotoEnd can stay false (default value) but you can try tochange it to true if you want, you might like the result.

Usage Example: jQuery('button#clickMe').stop(true).animate({left:+=10}).

Mike
  • 2,497
  • 3
  • 21
  • 34
  • It's not the fact they queue up, i prevent that with the :animated check. It's the fact they go out of sync with each other in more ways than one. – Scotty Aug 13 '09 at 13:27
3

you can put this first thing inside the click event

    $(element).css({ "pointer-events":"none"}); 
    
, and this in the callback function of the animation
    $(element).css({ "pointer-events":"auto"});
    
  • by far the simplest method. I found it also worth applying this to the entire container limiting any other conflicts while the animation runs. – Todd Padwick Sep 22 '17 at 09:47
0

you can unbind... but this should work too:

if (!$("#div2 .tab").is(':animated') && !$("#GroupDiv").is(':animated')) return;
Jubair
  • 2,557
  • 2
  • 26
  • 25
0

I have recently made an AJAX jQuery plugin, featuring plenty of animation. The workaround to the AJAX animation bug that I have found is as follows.

    $(options.linkSelector).click(function(e){
    if ($("#yourNav").hasClass("disabled")) {
      return false;
    } else {
      e.preventDefault();
      $("#yourNav").addClass("disabled")
      // Prepare DOM for new content
      $(content).attr('id', 'content-old');
      $('<div/>', {id: 'ajMultiLeft'}).css({'top': '100%'}).insertAfter('#content-old');

      // Load new content
      $(content).load(linkSrc+ ' ' +options.content+ ' > *', function() {

      // Remove old content

      $(content).animate({top: '100%'}, 1000, function(){
        $(content-old).remove(); 
        $("#yourNav").removeClass("disabled")
      });

      setBase();
    }

What this does is makes the click event for each link respond to nothing whilst the parent div has a class of disabled. The disabled class is set by the function upon initial click and removed via a callback on the final animation.

Alex Scott
  • 56
  • 8