0

I have a button that moves an element (changing its top position by 100px) and that transition takes time (1.5s).
I found that if the user clicks several times and very fast, in the button the the final position won't be the expected as it would start a new transition when still in the middle of the previous one.
My goal would be to make sure every button click will make the element move by 100px regardless of how fast a user clicks a button, so that next movement would only start after the previous one and so on. My code is:

    var extHeight = parseInt($(this).siblings('.screen').css('height'), 10);
    var intHeight = parseInt($(this).children('.wrap').css('height'), 10);
    var maxScroll = intHeight - extHeight;
    var pos = parseInt($(this).children('.wrap').css('top'), 10);
    if (maxScroll - 100 > -pos) {
      $(this).children('.wrap').css({'top': pos-100+'px'});
    }

I've tried using setTimeout and also a counter without success. PS: I'm using this inline.

Paulo Janeiro
  • 3,181
  • 4
  • 28
  • 46
  • Check out: "debounce immidiate" at: http://stackoverflow.com/questions/24004791/can-someone-explain-the-debounce-function-in-javascript – JonSG Oct 12 '16 at 17:50

3 Answers3

0

Try somethng like this:

$(this).children('.wrap').stop(true, true).css({'top': pos-100+'px'});

or other stop parameters

We can create a nice fade effect without the common problem of multiple queued animations by adding .stop(true, true) to the chain:

from here

Stop the currently-running animation on the matched elements.

If the clearQueue parameter is provided with a value of true, then the rest of the animations in the queue are removed and never run.

jack blank
  • 5,073
  • 7
  • 41
  • 73
0

This example waits for a certain amount of time to handle the next click event. You might want to set it for 1.5 seconds. It shows the use of the debounce method. It basically cancels out all the click events to the button for a certain amount of time until the event ( that handles the css positioning ) is finished.

function debounce(func, wait, immediate){
 var timeout;
 return function(){
  var context = this, args = arguments;

  var callNow = immediate && !timeout;

  clearTimeout(timeout);
  timeout = setTimeout(function(){
   timeout = null;

   if(!immediate){
    func.apply(context, args);
   }
  }, wait);

  if(callNow) func.apply(context, args)
 }
}

var myEfficientFn = debounce(function(){
 $(".box").css({"top": "+=100"})
}, 5000, true)

$("button").on("click", myEfficientFn)
.box{
  width:50px;
  height:50px;
  background: pink;
  position:relative;
  left: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div class="box"></div>
<button>button</button>
jack blank
  • 5,073
  • 7
  • 41
  • 73
0

I've found a solution that fulfils all my requirements (other responses didn't handle the execution of all clicks after ongoing transition ends). I define controlling variables in global scope:

var upClick = 0; var timelineIsMoving = false;

Then the code is (I'm using inline event):

onclick="$this = $(this);
        if (timelineIsMoving == true) {upClick = upClick + 1} else {upClickF()};
        function upClickF(){
          timelineIsMoving = true;
          if(upClick > 0) {upClick = upClick - 1};
          var pos = parseInt($this.children('.layer3.wrap').css('top'), 10);
          if (pos < 0) {
             $this.children('.layer3.wrap').css({'top': pos-100+'px'});
          };
          setTimeout(function(){
            timelineIsMoving = false;
            if (upClick > 0) {upClickF()}
          }, 1500)
        }

This uses a recursive function that runs only when the movement is ended and if there are queued clicks to be executed.

Paulo Janeiro
  • 3,181
  • 4
  • 28
  • 46