为什么在事件回调之前执行间隔函数?

时间:2014-09-05 12:11:05

标签: javascript scroll setinterval event-loop onscroll

我意识到关于滚动事件的javascript有些奇怪。

直到现在,我始终相信只要滚动位置发生变化,就会直接触发它 而且因为javascript是阻塞的,回调总是第一个被执行的东西和第一个“看到”这个新值的函数 - 或者我认为。

这里我有简单的设置,其中全局值在滚动时更新到当前滚动位置。 然后有一个比较两者的间隔 现在,如果您非常快地滚动,有时会发生缓存的值与实际返回的滚动位置不同。

// cache scroll pos
var scrollPos = 0;
// update on scroll
window.onscroll = function () {
    scrollPos = scrollTop();
};
// compare on interval
setInterval(function () {
    if (scrollPos != scrollTop()) {
        console.log("Out of sync!  cached:", scrollPos, "| actual:", scrollTop());
    }
}, 100);
小提琴:http://jsfiddle.net/71vdx4rv/2/
您可以尝试快速滚动或使用按钮。 [在Chrome,Safari,FF,Opera上测试]

为什么在回调之前执行间隔功能并知道'新'滚动位置?
有人可以向我解释一下吗?

1 个答案:

答案 0 :(得分:4)

  

直到现在,我始终相信只要滚动位置发生变化,就会直接触发它。

不幸的是(显然)事实并非如此。虽然DOM 3 does state

  

用户代理必须在滚动文档视图或元素时调度[{1}}]事件。滚动发生后将调度此事件类型。

这并不意味着“立即”。 CSSOM draft clarifies

  

[要执行滚动,请重复运行这些步骤]:

     
      
  1. 执行scrollbox的[平滑或即时]滚动。
  2.   
  3. 将任务排队以运行position,除非运行task的任务在队列中。
  4.         

    [其中task确实会在相应目标上发起task个事件

“排队任务”显式意味着事件是异步调度的,因此在滚动事件触发任务之前执行等待任务(如计时器)时,滚动位置可能已经被更改。

此外,在执行平滑滚动时,允许浏览器在任意时间跨度内多次更改滚动位置,然后排队任务以触发scroll事件。