Why does another scroll event get called after a scrollTop
animation fires its complete
callback?
Click Handler:
var lock = false;
$('#id').click(function(event) {
var pos;
if (lock) {
return;
}
lock = true;
pos = 150;
console.log("jump start");
$(jQuery.browser.webkit ? "body": "html").animate({ scrollTop: pos }, 150, function () {
lock = false;
console.log("jump end");
});
});
Scroll Handler:
$(window).scroll(function (e) {
console.log("scrolling");
if (!lock){
alert('1');
}
});
Log:
jump start
scrolling
jump end
scrolling
demo on jsfiddle
Background
jQuery scrollTop() uses scrollTo() which is a fire and forget event. There is no stopped event for scrolling. The scroll events occur out of band from scrollTo. scrollTo means 'start scroll', a scroll event means 'scrolled (some position)'. scrollTo just initiates the starting of the scroll, it doesn't guarantee that scrolling finished when it returns. So, jQuery animation completes before final scroll (there could even be multiple scrolls backed up). The alternative would be for jQuery to wait for the position of the scroll to be what it requested (per my soln), but it does not do this
It would be nice if there was a specification that we could point to describing this, but it is just one of the Level 0 dom elements without a spec, see here. I think it makes sense the way that it works, which is why all browsers seem to implement it this way.
Why is this happening
The following occurs on the last scroll of the animation:
lock = false;console.log("jump end");
$(window).scroll(function (e) {
'Why is this happening?'As you can see jquery does not wait for the final scroll step of the animation to complete before completing the animation (going on to step 4). Partly this is because there is no stopped event for scrolling and partly this is because jquery does not wait for the scroll position to reach the position that was requested. We can detect when we have reached the destination position as described below.
Solutions
There is no stopped event for when scrolling completes. See here. It makes sense that there is no stopped event because the user could start scrolling again at any point, so there is no point where scrolling has really stopped - the user might just have paused for a fraction of a second.
User scrolling: For user scrolling, the normal approach is to wait some amount of time to see if scrolling is complete as described in the answer of the referenced question (bearing in mind that the user could start scrolling again).
scrollTop: However, since we know the position that we are scrolling to we can do better.
See this fiddle.
The crux of it is that since we know where we are scrolling to, we can store that position. When we reach that position we know that we are done.
The output is now:
jump start
scroll animation
jump end
The code is (note that this is based off your fiddle rather than the code in the edited question):
var scrollingTo = 0;
$('#id').click(function(event) {
if (scrollingTo) {
return;
}
console.log("jump start");
scrollingTo = 150;
$(jQuery.browser.webkit ? "body": "html").animate({ scrollTop: scrollingTo }, 150, function () {
});
});
function handleScroll()
{
if( scrollingTo !== 0 && $(window).scrollTop() == scrollingTo)
{
scrollingTo = 0;
console.log("jump end");
}
}
$(window).scroll(function (e) {
if (!scrollingTo){
console.log('user scroll');
} else {
console.log("scroll animation");
}
handleScroll();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With