Skip to main content

Bug Tracker

Side navigation

#14820 closed bug (invalid)

Opened February 19, 2014 06:56PM UTC

Closed March 31, 2014 08:50AM UTC

Last modified March 31, 2014 01:36PM UTC

scroll event fires after scrollTop animation is complete

Reported by: zzzzbov@gmail.com Owned by: zzzzbov@gmail.com
Priority: undecided Milestone: None
Component: unfiled Version: 2.1.0
Keywords: Cc:
Blocked by: Blocking:
Description

Animations that animate the scrollTop property trigger scroll events, however it appears that an additional scroll event is triggered ''after'' the animation's complete callback has been called.

http://jsfiddle.net/LMR4d/

I'm experiencing this in Chrome, Firefox, and IE10 on Windows 7.

Attachments (0)
Change History (10)

Changed March 06, 2014 07:53AM UTC by voelzmo@gmail.com comment:1

Does animate() really return a deferred? If I try your example with a self-created Deferred and the 'complete' callback, it seems to work:

function animate(){
  var deferred = new jQuery.Deferred();
  $('html, body').animate({
    scrollTop: 500
  }, {
    complete : function() {
        deferred.resolve();
    },
    duration : 3000
  });  
  return deferred;
}

Changed March 06, 2014 08:04AM UTC by anonymous comment:2

Replying to [comment:1 voelzmo@…]:

Argh, nevermind. I was of course forgetting the most important thing: setting animatingScroll = false in the complete callback. Then I see the exact same behavior, see http://jsfiddle.net/89pnX/.

PS: We're on jQuery 1.7.2 so this problem seems to have been around for a while.

Changed March 06, 2014 08:08AM UTC by voelzmo comment:3

_comment0: Replying to [comment:2 anonymous]: \ Aaaaand here is the correct URL, sorry for the mess: http://jsfiddle.net/89pnX/2/ \ 1394093665666810
_comment1: Replying to [comment:2 anonymous]: \ Aaaaand here is the correct URL, sorry for the mess: http://jsfiddle.net/89pnX/3/ \ 1394093752337769

Replying to [comment:2 anonymous]:

Aaaaand here is the correct URL, sorry for the mess: http://jsfiddle.net/89pnX/3/

A possible workaround seems to be to delay the execution of the complete callback by a magic number: http://jsfiddle.net/89pnX/4/

Changed March 06, 2014 09:19PM UTC by zzzzbov@gmail.com comment:4

Replying to [comment:1 voelzmo@…]:

Does animate() really return a deferred? If I try your example with a self-created Deferred and the 'complete' callback, it seems to work:
> function animate(){
>   var deferred = new jQuery.Deferred();
>   $('html, body').animate({
>     scrollTop: 500
>   }, {
>     complete : function() {
>         deferred.resolve();
>     },
>     duration : 3000
>   });  
>   return deferred;
> }
> 

animate() doesn't return a deferred, promise() on a jQuery collection returns a deferred that resolves when ''all'' 'fx' queues have completed. The provided example code resolves when the ''first'' 'fx' queue has completed.

Changed March 06, 2014 09:32PM UTC by zzzzbov@gmail.com comment:5

It appears as though this isn't actually a bug with jQuery, and instead seems to be a quirk of scroll events in the browser. See this related question on StackOverflow for additional details.

Assigning a new scroll value does not immediately queue a scroll event. Instead, there can be an unknown amount of delay between setting scrollTop and the scroll event firing (http://jsfiddle.net/jSBn2/). Sometimes the scroll event will be immediately queued in the event loop, sometimes not.

This delay is what's causing the odd behavior of the scroll event after the complete callback.

Changed March 06, 2014 09:55PM UTC by dmethvin comment:6

I thought this was what happened: When you set scrollTop it queues an event to occur once the thread completes. On the final step of the animation the Deferred resolves synchronously and immediately calls all the callbacks. Then the thread completes and the event fires.

Changed March 06, 2014 10:07PM UTC by zzzzbov@gmail.com comment:7

Replying to [comment:6 dmethvin]:

I thought this was what happened: When you set scrollTop it queues an event to occur once the thread completes. On the final step of the animation the Deferred resolves synchronously and immediately calls all the callbacks. Then the thread completes and the event fires.

Promises aren't supposed to resolve synchronously, although I'm aware that jQuery's deferreds deviate from the Promises/A+ spec. Even if that were the case, including a delay(0) call into the chain before the promise should fix the issue, which it doesn't.

Changed March 16, 2014 03:51PM UTC by dmethvin comment:8

owner: → zzzzbov@gmail.com
status: newpending

Oh, I see you were animating two things and then using the per-element callback. That's not going to work consistently anyway. See the discussion here: https://github.com/jquery/api.jquery.com/issues/417

Does that docs issue answer your question?

Changed March 31, 2014 08:50AM UTC by trac-o-bot comment:9

resolution: → invalid
status: pendingclosed

Because we get so many tickets, we often need to return them to the initial reporter for more information. If that person does not reply within 14 days, the ticket will automatically be closed, and that has happened in this case. If you still are interested in pursuing this issue, feel free to add a comment with the requested information and we will be happy to reopen the ticket if it is still valid. Thanks!

Changed March 31, 2014 01:36PM UTC by zzzzbov@gmail.com comment:10

Replying to [comment:8 dmethvin]:

Oh, I see you were animating two things and then using the per-element callback.

I'm not sure I follow. I animated two things and set up a promise to fire once when both fx queues were clear.

voelzmo's snippet uses the per-animation callback, maybe that's what you were referring to.

Either way, I found that the scroll event doesn't fire synchronously after scrollTop is changed, which is what is causing the erratic behavior. As it's a native browser behavior, it's not a bug in jQuery, so this ticket can remain closed.