Programing

스크롤 이벤트가 너무 많이 발생합니다.

lottogame 2020. 12. 12. 09:58
반응형

스크롤 이벤트가 너무 많이 발생합니다. 초당 최대 한 번만 발사하고 싶습니다.


"무한 스크롤"페이지가 있습니다. 페이지 끝과 현재 페이지의 차이를 계산하고이 차이가 충분히 작 으면 더 많은 콘텐츠를로드합니다. 코드는 jQuery를 사용하여 다음과 같습니다.

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

이제 문제는 스크롤 할 때마다이 이벤트가 스크롤 당 여러 번 발생한다는 것입니다. 나는 기껏해야 x 밀리 초마다 발사하고 싶습니다. 어떻게해야합니까?


Underscore.js 라이브러리의 "throttle"메소드를 확인하십시오.

http://underscorejs.org/#throttle

이것이 제공하는 예제는 스크롤 이벤트를 처리해야하는 빈도를 제한하는 것입니다.


이 문제를 해결하는 한 가지 방법은 시간 간격을 정의하고 해당 시간 간격 내에서 스크롤 이벤트를 한 번만 처리하는 것입니다. 해당 시간 간격 동안 스크롤 이벤트가 두 개 이상 들어 오면 무시하고 해당 시간 간격이 지난 경우에만 처리합니다.

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으로 설정된 값) 으로 변경하여 이벤트 빈도를 조정할 수 있습니다 .

여기에 데모가 있습니다. http://jsfiddle.net/jfriend00/EBEqZ/ 디버그 콘솔 창을 열고 콘텐츠 창에서 스크롤바를 이동 한 다음 디버그 콘솔 창에서 각 스크롤 이벤트의 시간을 확인해야합니다. . 내 버전의 Chrome에서는 최소 간격이 100ms로 설정되어 있으며 100-200ms마다 발생하는 것 같습니다.


이 상황을 해결하기 위해 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);

출처 : http://ejohn.org/blog/learning-from-twitter/


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

또는

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

다음과 같이 부를 수 있습니다.

$('body').on('mousemove', throttle(function (event) {
  console.log('tick');
}, 1000));

다음은 단순성을 목표로하는 추가 JS 라이브러리 또는 플러그인을 사용할 필요가없는 솔루션입니다. 다른 구현만큼 효율적이지 않을 수 있지만 스크롤 할 때마다 메인 이벤트를 시작하는 것보다 확실히 한 단계 더 올라갑니다.

이것은 Danny Van Kooten 의이 블로그 게시물 에서 가져온 것입니다 . onscroll()내 블로그의 back-to-top 버튼에 대한 이벤트를 지연시키는 데 사용했습니다 .

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

이것은 실제 기능이 150ms마다 실행되도록 제한하거나 150ms가 지나지 않은 경우 타이머를 0으로 재설정합니다. 필요한 것에 맞게 값을 조정하십시오.


스크롤이 여러 번 발생하면 매번 스크롤 위치를 다르게 얻을 수 있습니다. 언급 한 것처럼 스크롤 이벤트에 처음 들어갈 때 타이머를 설정하고 타임 스탬프를 기록한 다음 다음에 스크롤 이벤트가 발생하면 마지막 트리거 시간을 확인하고 x 밀리 초 이내에 있으면 무시해야한다고 생각합니다. , 타이머 작업에서 실제 작업을 수행하십시오.


적절한 스로틀 기능을 위해 많은 지역 변수가 필요하지 않습니다. 스로틀 기능의 목적은 더 많이 사용하는 오버 헤드를 너무 많이 적용하는 것이 아니라 브라우저 리소스를 줄이는 것입니다. 이 주장의 증거로서, 나는 그 범위에 단지 5 개의 '매달려있는'변수 리퍼 렌을 갖는 스로틀 함수를 고안했습니다. 또한 스로틀 기능에 대한 저의 다른 용도에는 여러 가지 상황이 필요합니다. 여기에 '좋은'스로틀 기능이 필요하다고 생각하는 것들의 목록이 있습니다.

  • 마지막 호출 이후 간격 MS 이상이면 즉시 함수를 호출합니다.
  • 다른 간격 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);
}

사용 예 :

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>

기본적으로 이는 200ms마다 최대 한 번의 호출로 함수를 제한합니다. 간격을 다른 밀리 초 수로 변경하려면 옵션 인수에 "interval"이라는 키를 전달하고 원하는 밀리 초로 설정합니다.

참고 URL : https://stackoverflow.com/questions/9613594/scroll-event-firing-too-many-times-i-only-want-it-to-fire-a-maximum-of-say-on

반응형