如何节俭使用JS DOM调用onscroll函数



我想达到的目标:

  • 如果用户从顶部(原点)滚动超过24px,做一些事情。
  • 如果用户在顶部(原点)的24px范围内滚动,则反转一次。

我有这样的代码:

$("#box").scroll(function(){
    var ofs = $(".title").offset().top;
    if (ofs <= 24)
        // Do something
    else
        // Reverse that something
})
根据我的理解,这个函数在每次用户滚动时都运行,这可能导致对DOM的数百次调用。
  1. 这不是每一个资源效率-有更被动的方法吗?
  2. 两个条件都被反复触发,即使是小的滚动量-如果相同的条件是真的,任何方法执行代码只一次而不重复?

您想要做的是限制请求或所谓的"debounce"。Throttling只允许在一段时间内调用一定数量的函数,debounce只在动作停止后的一段时间内调用该函数。

这是一个很好的解释链接:https://css-tricks.com/the-difference-between-throttling-and-debouncing/

有几个库可以为你做这个,比如Underscore和Lodash。你也可以滚动你自己的,debounce的基本前提是:

var timer;
$('#box').scroll(function(){
    //cancel and overwrite timer if it exists already
    // set timer to execute doWork after x ms
})
function doWork(){
    //do stuff
}

你也可以考虑使用requestAnimationFrame取决于浏览器的支持。requestAnimationFrame的例子,它看起来支持大多数现代浏览器和IE>= 10

在下面的代码中,每次用户滚动超过或低于25px阈值时,将调用if ($boxAboveBelow) if语句中的一个条件。

var $box = $('#box');
var $boxAboveBelow = true; // true above, false below
$box.on('scroll', function() { // Throttle this function if needed
    var newAboveBelow = $box.scrollTop() < 25;
    if (newAboveBelow !== $boxAboveBelow) {
        $boxAboveBelow = newAboveBelow;
        if ($boxAboveBelow) {
            // If the user scrolls back within 24px of the top (origin), reverse that something once.
        } else {
            // If user has scrolled more than 24px from the top (origin), do something once.
        }
    }
})

如果你需要这些条件只被调用一次,你可以设置布尔变量来记录这些条件是否被调用过。

var aboveCalled = false;
var belowCalled = false;
var $box = $('#box');
var $boxAboveBelow = true; // true above, false below
$box.on('scroll', function() { // Throttle this function if needed
    var newAboveBelow = $box.scrollTop() < 25;
    if (newAboveBelow !== $boxAboveBelow) {
        $boxAboveBelow = newAboveBelow;
        if ($boxAboveBelow) {
            !aboveCalled && doScrollAboveStuff();
            aboveCalled = true;
        } else {
            !belowCalled && doScrollBelowStuff();
            belowCalled = true;
        }
    if (aboveCalled && belowCalled) {
        $box.off('scroll'); // No need to keep listening, since both called
    }
});

最新更新