Javascript 滚动事件触发次数过多。我只希望它最多每秒发射一次

Javascript 滚动事件触发次数过多。我只希望它最多每秒发射一次,javascript,Javascript,我有一个“无限卷轴”的页面。它计算页面结尾与当前页面之间的差异,如果差异足够小,则加载更多内容。使用jQuery时,代码如下所示: $(window).on('scroll', function() { if (window.pageYOffset > loadMoreButton.offsetTop - 1000) # load more content via ajax } 现在,问题是每次我滚动时,每个滚动都会触发多次此事件。我希望最多每x毫秒开火一次。我该

我有一个“无限卷轴”的页面。它计算页面结尾与当前页面之间的差异,如果差异足够小,则加载更多内容。使用jQuery时,代码如下所示:

$(window).on('scroll', function() {
    if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
        # load more content via ajax
}

现在,问题是每次我滚动时,每个滚动都会触发多次此事件。我希望最多每x毫秒开火一次。我该如何做到这一点?

查看underline.js库的“throttle”方法


它给出的示例正是您所要求的-限制您必须处理滚动事件的频率。

多次滚动触发是正确的,您应该能够每次获得不同的滚动位置。我认为当你第一次进入滚动事件时,你需要设置一个计时器,就像你提到的x毫秒,同时记录时间戳,然后下次滚动事件触发时,检查最后的触发时间,如果在x毫秒内,则忽略它,解决此问题的一种方法是定义一个时间间隔,并在该时间间隔内仅处理一次滚动事件。如果在该时间间隔内出现多个滚动事件,则忽略它并仅在该时间间隔结束时处理它

var isWorking = 0;

$(window).on('scroll', function()
{
    if(isWorking==0)  
    {
         isWorking=1;
         if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
         # load more content via ajax
         setTimeout(function(){isWorking=0},1000);
    }
}
var scrollTimer, lastScrollFireTime = 0;

$(window).on('scroll', function() {

    var minScrollTime = 100;
    var now = new Date().getTime();

    function processScroll() {
        console.log(new Date().getTime().toString());
    }

    if (!scrollTimer) {
        if (now - lastScrollFireTime > (3 * minScrollTime)) {
            processScroll();   // fire immediately on first scroll
            lastScrollFireTime = now;
        }
        scrollTimer = setTimeout(function() {
            scrollTimer = null;
            lastScrollFireTime = new Date().getTime();
            processScroll();
        }, minScrollTime);
    }
});
这将立即触发第一个滚动事件,然后在滚动条移动时大约每100ms触发一次滚动事件,然后在滚动条停止移动后触发最后一个事件。您可以通过将参数更改为
setTimeout
(当前设置为100)来调整事件的频率

这里有一个演示:您需要打开调试控制台窗口,开始在内容窗口中移动滚动条,然后在调试控制台窗口中观察每个滚动事件的时间。在我的Chrome版本中,它们的最小间隔设置为100ms,并且似乎每100-200ms出现一次

var now = new Date().getTime();
$(window).scroll( function () {
    if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
    {
        if (new Date().getTime() - now > 1000)
        {
            console.log("Task executed once per second");
            now = new Date().getTime();
        }
    }
});

您可以使用节流功能调用:


这里有一个解决方案,它不需要使用额外的JS库或插件,目的是简单。它可能没有其他实现那么有效,但它肯定比每次滚动时触发主事件更有效

这是Danny Van Kooten从这张照片上取的。我用它来延迟我博客上返回顶部按钮的
onscroll()
事件

var timer;
$(window).scroll(function() {
    if(timer) {
        window.clearTimeout(timer);
    }
    timer = window.setTimeout(function() {
       // actual code here. Your call back function.
    console.log( "Firing!" );
    }, 100);
});
您还可以通过将变量移出回调函数来进一步提高性能,以避免不必要的重新计算,例如
$(window).height()
的值或加载页面后不会更改的某些静态div元素的高度

下面是一个根据我的用例改编的示例

var scrollHeight = $("#main-element").height(); //never changes, no need to recalculate.
$(window).on('scroll', function() {
    if (timer) 
        window.clearTimeout(timer);
    timer = window.setTimeout(function() {
        var scrollPosition = $(window).height() + $(window).scrollTop();    
        if ($(window).scrollTop() < 500)
            $(".toggle").fadeIn(800);
        else 
            $(".toggle").fadeOut(800);
    }, 150); //only fire every 150 ms.
});
var scrollHeight=$(“#main元素”).height()//永不改变,无需重新计算。
$(窗口).on('scroll',function(){
中频(定时器)
清除超时(计时器);
timer=window.setTimeout(函数(){
var scrollPosition=$(窗口).height()+$(窗口).scrollTop();
如果($(窗口).scrollTop()<500)
$(“.toggle”).fadeIn(800);
其他的
$(“.toggle”).fadeOut(800);
},150);//仅每150毫秒点火一次。
});

这将实际功能限制为仅每150ms执行一次,否则,如果未超过150ms,则将计时器重置回0。调整值以满足您的需要

jQuery的创建者John Resig对此给出了一个冷静的解释

var outerPane = $details.find(".details-pane-outer"),
    didScroll = false;

$(window).scroll(function() {
    didScroll = true;
});

setInterval(function() {
    if ( didScroll ) {
        didScroll = false;
        // Check your page position and then
        // Load in more results
    }
}, 250);
资料来源:
一个好的油门功能不需要大量的局部变量。节流功能的目的是减少浏览器资源,而不是应用太多的开销,使您使用的浏览器资源更多。为了证明这一点,我设计了一个节流函数,它的作用域中只有5个“挂起”变量引用。此外,我对油门功能的不同使用需要许多不同的环境。这是我的清单,我相信'好'油门功能需要的东西

  • 如果自上次调用以来间隔超过毫秒,则立即调用该函数
  • 避免为另一个间隔MS执行函数
  • 延迟过多的事件触发,而不是完全删除事件
  • 在连续调用时更新延迟事件对象,使其不会变为“过时”
我相信下面的油门功能满足所有这些条件

function throttle(func, alternateFunc, minimumInterval) {
    var executeImmediately = true, freshEvt = null;
    return function(Evt) {
        if (executeImmediately) { // Execute immediatly
            executeImmediately = false;
            setTimeout(function(f){ // handle further calls
                executeImmediately = true;
                if (freshEvt !== null) func( freshEvt );
                freshEvt = null;
            }, minimumInterval);
            return func( Evt );
        } else { // Delayed execute
            freshEvt = Evt;
            if (typeof alternateFunc === "function") alternateFunc( Evt );
        }
    };
}
然后,要围绕DOM事件侦听器包装此节流函数,请执行以下操作:

var ltCache = [];
function listen(obj, evt, func, _opts){
    var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
    a: {
        for (; i < Len; i += 4)
            if (ltCache[i] === func &&
              ltCache[i+1] === (options.alternate||null) &&
              ltCache[i+2] === (options.interval||200)
            ) break a;
        lF = throttle(func, options.alternate||null, options.interval||200);
        ltCache.push(func, options.alternate||null, options.interval||200, lF);
    }
    obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
    for (var i = 0, Len = ltCache.length; i < Len; i += 4)
        if (ltCache[i] === func &&
          ltCache[i+1] === (options.alternate||null) &&
          ltCache[i+2] === (options.interval||200)
        ) return obj.removeEventListener(evt, ltCache[i+3], options);
}
var ltCache=[];
函数侦听(obj、evt、func、_opts){
var i=0,Len=ltCache.length,lF=null,options=_opts |{};
a:{
对于(;i
用法示例:

功能节流阀(func、alternateFunc、minimumInterval){
var executeImmediately=true,freshvt=null;
函数handleFurtherCalls(f){
executeImmediately=true;
if(freshvt!==null)func(freshvt);
freshvt=null;
};
返回函数(Evt){
如果(executeImmediately){//立即执行
executeImmediately=false;
设置超时(handleFurtherCalls,minimumInterval);
function throttle(func, alternateFunc, minimumInterval) {
    var executeImmediately = true, freshEvt = null;
    return function(Evt) {
        if (executeImmediately) { // Execute immediatly
            executeImmediately = false;
            setTimeout(function(f){ // handle further calls
                executeImmediately = true;
                if (freshEvt !== null) func( freshEvt );
                freshEvt = null;
            }, minimumInterval);
            return func( Evt );
        } else { // Delayed execute
            freshEvt = Evt;
            if (typeof alternateFunc === "function") alternateFunc( Evt );
        }
    };
}
var ltCache = [];
function listen(obj, evt, func, _opts){
    var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
    a: {
        for (; i < Len; i += 4)
            if (ltCache[i] === func &&
              ltCache[i+1] === (options.alternate||null) &&
              ltCache[i+2] === (options.interval||200)
            ) break a;
        lF = throttle(func, options.alternate||null, options.interval||200);
        ltCache.push(func, options.alternate||null, options.interval||200, lF);
    }
    obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
    for (var i = 0, Len = ltCache.length; i < Len; i += 4)
        if (ltCache[i] === func &&
          ltCache[i+1] === (options.alternate||null) &&
          ltCache[i+2] === (options.interval||200)
        ) return obj.removeEventListener(evt, ltCache[i+3], options);
}