Scroll event firing too many times. I only want it to fire a maximum of, say, once per second

后端 未结 8 634
再見小時候
再見小時候 2020-11-27 12:24

I have a page with \"infinite scroll\". It calculates the difference between the end of the page and the current page and loads more content if this difference is small enou

相关标签:
8条回答
  • 2020-11-27 12:42

    One way to solve this problem is to define a time interval and only process a scroll event once within that time interval. If more than one scroll event comes in during that time interval, you ignore it and process it only when that time interval has passed.

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

    This will fire the first scroll event immediately and then get you a scroll event approximately once every 100ms while the scrollbar is being moved and then one final event after the scrollbar stops moving. You can adjust the frequency of the event by changing the argument to the setTimeout (what is currently set to 100).

    There is a demo here: http://jsfiddle.net/jfriend00/EBEqZ/ which you need to open a debug console window, start moving the scrollbar in the content window and then watch the time of each scroll event in the debug console window. On my version of Chrome, they are set for a minimum spacing of 100ms and they seem to occur every 100-200ms.

    0 讨论(0)
  • 2020-11-27 12:46
    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 12:46
    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();
            }
        }
    });
    

    Or

    You can use Throttling fonction calls: throttling-function-calls

    function throttle(fn, threshhold, scope) {
      threshhold || (threshhold = 250);
      var last,
          deferTimer;
      return function () {
        var context = scope || this;
    
        var now = +new Date,
            args = arguments;
        if (last && now < last + threshhold) {
          // hold on to it
          clearTimeout(deferTimer);
          deferTimer = setTimeout(function () {
            last = now;
            fn.apply(context, args);
          }, threshhold);
        } else {
          last = now;
          fn.apply(context, args);
        }
      };
    }
    

    You can call it like this:

    $('body').on('mousemove', throttle(function (event) {
      console.log('tick');
    }, 1000));
    
    0 讨论(0)
  • 2020-11-27 12:47

    One does not need a ton of local variables for a decent throttle function. The purpose of a throttle function is to reduce browser resources, not to apply so much overhead that you are using even more. As proof of evidence of this claim, I have devised a throttle function that has only 5 'hanging' variables referenes in its scope. Additionally, my different uses for throttle functions require many different circumstances for them. Here is my list of things that I believe 'good' throttle function needs.

    • Immediately calls the function if it has been more than interval MS since the last call.
    • Avoids executing function for another interval MS.
    • Delays excessive event firing instead of dropping the event altogether.
    • Updates the delayed event object on successive calls so that it doesn't become 'stale'.

    And, I believe that the following throttle function satisfies all of those.

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

    Then, to wrap this throttle function around DOM event listeners:

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

    Example usage:

    function throttle(func, alternateFunc, minimumInterval) {
        var executeImmediately = true, freshEvt = null;
        function handleFurtherCalls(f){
            executeImmediately = true;
            if (freshEvt !== null) func( freshEvt );
            freshEvt = null;
        };
        return function(Evt) {
            if (executeImmediately) { // Execute immediatly
                executeImmediately = false;
                setTimeout(handleFurtherCalls, 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);
    }
    var numScrolls = 0, counter = document.getElementById("count");
    listen(window, 'scroll', function whenbodyscrolls(){
        var scroll = -document.documentElement.getBoundingClientRect().top;
        counter.textContent = numScrolls++;
        if (scroll > 900) {
          console.log('Body scrolling stoped!');
          mute(window, 'scroll', whenbodyscrolls, true);
        }
    }, true);
    <center><h3>\/ Scroll Down The Page \/</h3></center>
    <div style="position:fixed;top:42px"># Throttled Scrolls: <span id="count">0</span></div>
    <div style="height:192em;background:radial-gradient(circle at 6em -5em, transparent 0px, rgba(128,0,0,.4) 90em),radial-gradient(circle at 10em 40em, rgba(255,255,255,.8) 0px, rgba(128,0,0,.02) 50em),radial-gradient(circle at 4em 80em, rgba(0,192,0,.75) 0px,rgba(0,128,0,.56) 10em,rgba(255,0,96,.03125) 30em),radial-gradient(circle at 86em 24em, rgba(255,0,0,.125) 0px,rgba(0,0,255,.0625) 60em,transparent 80em);"></div>
    <style>body{margin:0}</style>

    By default, this throttles the function to at most one call every 200ms. To change the interval to a different number of milliseconds, then pass a key named "interval" in the options argument and set it to the desired milliseconds.

    0 讨论(0)
  • 2020-11-27 12:56

    There is a cool explanation from John Resig, the creator of jQuery to resolve this situation.

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

    The source: http://ejohn.org/blog/learning-from-twitter/

    0 讨论(0)
  • 2020-11-27 13:01

    Check out the Underscore.js library's "throttle" method.

    http://underscorejs.org/#throttle

    The example it gives is exactly what you're asking about - limiting how often you have to handle scroll events.

    0 讨论(0)
提交回复
热议问题