views:

201

answers:

5

I'm using jquery and what I'm doing is binding the toggle method to a number of buttons on a webpage. It looks something like this

$('.button').toggle(function(){
  // first function
}, function(){
  // second function
});

However, there are animation in both of those functions. So a user can click the button while the first or second function is executing. And this messes up the order of the HTML elements and may make them move to the end of the page. Because essentially what these functions do is move one element to the end on the first click, and on the other click move it back where it originally was.

Of course, it is difficult to click the button once it is moving around the page. But it's possible.

+5  A: 

You could use a flag. Set a flag 'isAnimating' to true when an animation begins, and false when it ends. Any subsequent animation can only proceed if this value is false.

You could also possibly check to see if the :animated selector applies to the owner of the event. And base your decisions off of that.

Jonathan Sampson
Could this work with toggle() though? If I stop function one from executiing because there's an animation function two, then the next time the user clicks the button it'll go to function two again.
this is a dead end
What I'm suggesting is that you put an `if-statement` at the beginning of each function. If `isAnimating` is true, you leave the function. If it's false, you continue with the function. Is that not what you're looking for?
Jonathan Sampson
I know what you mean. I was just wondering if I could do this while still keeping the toggle() function there. But I'll have to get rid of it and use my own if to decide what function to use.
this is a dead end
Using the :animated selector is a good idea. I was going to suggest adding a class to the target for the duration of the animation, but :animated is better.
austinfromboston
Why do you need to get rid of the toggle function?
Jonathan Sampson
+3  A: 

You could use a bool as a semiphore.. Obviously, this is in no way secure, but javascript doesn't really support locking, so you could easily have deadlocks and / or race conditions with this approach, but it will work 99,9% of the times :)

cwap
A: 

You need to place the two functions you pass to toggle in a context in which you can hold a flag to control function entrance:-

(function() {
  var toggling = false;
  $('.button').toggle(function(){
    if (!toggling) {
      toggling = true;           
      // first function
      toggling = false;
    } else {
      // whatever you want to happen if re-entrance attempted
    }
  }, function(){
    if (!toggling) {
      toggling = true;           
      // second function
      toggling = false;
    } else {
      // whatever you want to happen if re-entrance attempted
    }
  })
 )();

N.B. This serialises all toggles of elements that have the .button class. IOW there is only one toggling flag for all buttons. If you want each button to have its own toggling flag:-

$('.button').each(function() {
  var toggling = false;
  $(this).toggle(function(){
    if (!toggling) {
      toggling = true;           
      // first function
      toggling = false;
    } else {
      // whatever you want to happen if re-entrance attempted
    }
  }, function(){
    if (!toggling) {
      toggling = true;           
      // second function
      toggling = false;
    } else {
      // whatever you want to happen if re-entrance attempted
    }
  });
 );
AnthonyWJones
Thanks. Although the logic is a bit wrong. If a use clicks the button and then clicks it again during animation, the second toggle function will trigger but nothing happen because of the if(!toggling). Then the next time they click it, the button will be at the bottom of the page but the first function will trigger. Thanks though, all I need to do is put the if statement before toggle().
this is a dead end
@Roly: It wasn't clear from your question what your logic was but the example is designed to demonstrate how you could create the appropriate contexts to hold the flag needed. I was sure you could bend the example to your specific logic as needed.
AnthonyWJones
A: 

Seems like you'll be happier implementing your own toggle. Toggle really only works for cases with 0 additional logic.

$('.button').click( 
function () {
  if( $(self).is(":animated") {
    return false;
  }
  if($(self).is(".rolledup")) {
     self.apply(roll_window_down);    
  } else {
     self.apply(roll_window_up);    
  }
});



function roll_window_up() {
  $(self).addClass( 'rolledup' );

  // first function    
}

function roll_window_down() {
  $(self).removeClass( 'rolledup' );
  // first function    
}
austinfromboston
I think this is what I'll do.
this is a dead end
Then mark it as accepted?
anddoutoi
A: 

You need a queue. You can build one with a semaphore variable, but jQuery already provides one, so maybe you want to use it:

$('.button').toggle(function() {
  $(document).queue("foo", function() {
    ...
  });
}, function() {
  $(document).queue("foo", function() {
    ...
  });
});

jQuery normally uses the "fx" queue to serialize animations, but you can use this "foo" queue for whatever you want.

The queue can be put on any object, so maybe you want to put it on the container that has all the .button objects in it. You cannot put it on the button (this) themselves, or you'll be back to where you're at now.

Once you've done that, all you really need to do is abort an animation. This can be done by expressly emptying the "fx" queue, or you can use $('.button').stop(); to stop all the old animations.

geocar