5

为什么动画触发回调会调用另一个滚动事件?scrollTopcomplete

单击处理程序:

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");
    });
});

滚动处理程序:

$(window).scroll(function (e) {
    console.log("scrolling");

    if (!lock){
        alert('1');
    }
});

日志:

jump start
scrolling
jump end
scrolling

jsfiddle 上的演示

4

2 回答 2

11

背景

jQuery scrollTop() 使用 scrollTo() 这是一个触发后忘记事件。滚动没有停止事件。滚动事件发生在 scrollTo 的带外。scrollTo 表示“开始滚动”,滚动事件表示“滚动(某个位置)”。scrollTo 只是启动滚动的开始,它不保证在它返回时滚动完成。因此,jQuery 动画在最终滚动之前完成(甚至可能有多个滚动备份)。另一种方法是让 jQuery 等待滚动的位置是它所请求的(根据我的解决方案),但它不会这样做

如果有一个规范可以用来描述这一点,那就太好了,但它只是没有规范的 0 级 dom 元素之一,请参见此处。我认为它的工作方式是有意义的,这就是为什么所有浏览器似乎都以这种方式实现它的原因。

为什么会这样

在动画的最后一个滚动发生以下情况:

  1. jquery: '窗口请滚动最后一点'
  2. 窗口:'我从 jquery 得到这条消息滚动我现在开始'
  3. jquery: '哇哦我完成了动画,我会完成'
  4. 你的代码: lock = false;console.log("jump end");
  5. 窗口:“我已经滚动”调用滚动事件处理程序。
  6. 您的代码:$(window).scroll(function (e) {“为什么会这样?”

如您所见,jquery 在完成动画之前不会等待动画的最后滚动步骤完成(继续第 4 步)。部分原因是没有停止滚动事件,部分原因是 jquery 不等待滚动位置到达请求的位置。我们可以检测何时到达目的地位置,如下所述。

解决方案

滚动完成时没有停止事件。见这里。没有停止事件是有道理的,因为用户可以在任何时候再次开始滚动,所以滚动没有真正停止的点——用户可能只是暂停了几分之一秒。

用户滚动:对于用户滚动,通常的方法是等待一段时间以查看滚动是否完成,如引用问题的答案中所述(请记住,用户可以再次开始滚动)。

scrollTop:但是,由于我们知道滚动到的位置,我们可以做得更好。

看到这个小提琴。

关键在于,既然我们知道滚动到哪里,我们就可以存储那个位置。当我们到达那个位置时,我们知道我们已经完成了。

现在的输出是:

jump start
scroll animation
jump end

代码是(请注意,这是基于您的小提琴而不是已编辑问题中的代码):

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();
});
于 2014-02-22T03:16:20.923 回答
0

我相信在动画结束并调用回调函数时,事件还没有到达window,所以它没有被重新调用,它只是还没有被触发。

于 2014-02-19T17:54:14.230 回答