jQuery scroll () ตรวจจับเมื่อผู้ใช้หยุดเลื่อน


109

โอเคกับเรื่องนี้ ..

$(window).scroll(function()
{
    $('.slides_layover').removeClass('showing_layover');
    $('#slides_effect').show();
});

ฉันสามารถบอกได้เมื่อมีคนเลื่อนจากสิ่งที่ฉันเข้าใจ ด้วยเหตุนี้ฉันจึงพยายามหาวิธีจับเมื่อมีคนหยุด จากตัวอย่างข้างต้นคุณจะเห็นว่าฉันกำลังลบคลาสออกจากชุดขององค์ประกอบในขณะที่กำลังเกิดการเลื่อน อย่างไรก็ตามฉันต้องการเปิดคลาสนั้นอีกครั้งเมื่อผู้ใช้หยุดเลื่อน

เหตุผลนี้คือฉันตั้งใจที่จะให้มีการแสดงการหยุดพักระหว่างที่หน้ากำลังเลื่อนเพื่อให้หน้าเว็บมีเอฟเฟกต์พิเศษที่ฉันกำลังพยายามทำอยู่ แต่คลาสเดียวที่ฉันพยายามลบในขณะที่การเลื่อนขัดแย้งกับเอฟเฟกต์นั้นเนื่องจากเอฟเฟกต์ความโปร่งใสต่อธรรมชาติบางอย่าง



ยอดเยี่ยมไม่ซ้ำกันทุกประการ แต่เป็นส่วนสำคัญของสิ่งที่ฉันกำลังมองหาและช่วยฉันแก้ปัญหาได้ในที่สุด ขอบคุณ.
chris

คำตอบ:


253
$(window).scroll(function() {
    clearTimeout($.data(this, 'scrollTimer'));
    $.data(this, 'scrollTimer', setTimeout(function() {
        // do something
        console.log("Haven't scrolled in 250ms!");
    }, 250));
});

อัปเดต

ฉันเขียนส่วนขยายเพื่อปรับปรุงค่าเริ่มต้นของ jQueryon -event-handlerมันแนบฟังก์ชันตัวจัดการเหตุการณ์สำหรับเหตุการณ์อย่างน้อยหนึ่งเหตุการณ์เข้ากับองค์ประกอบที่เลือกและเรียกใช้ฟังก์ชันตัวจัดการหากเหตุการณ์ไม่ได้ถูกทริกเกอร์สำหรับช่วงเวลาที่กำหนด สิ่งนี้มีประโยชน์หากคุณต้องการเริ่มการเรียกกลับหลังจากเกิดความล่าช้าเท่านั้นเช่นเหตุการณ์ปรับขนาดหรืออื่น ๆ

สิ่งสำคัญคือต้องตรวจสอบ github-repo สำหรับการอัปเดต!

https://github.com/yckart/jquery.unevent.js

;(function ($) {
    var on = $.fn.on, timer;
    $.fn.on = function () {
        var args = Array.apply(null, arguments);
        var last = args[args.length - 1];

        if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);

        var delay = args.pop();
        var fn = args.pop();

        args.push(function () {
            var self = this, params = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                fn.apply(self, params);
            }, delay);
        });

        return on.apply(this, args);
    };
}(this.jQuery || this.Zepto));

ใช้มันเหมือนกับตัวจัดการอื่น ๆonหรือbind-event ยกเว้นว่าคุณสามารถส่งผ่านพารามิเตอร์พิเศษเป็นตัวสุดท้าย:

$(window).on('scroll', function(e) {
    console.log(e.type + '-event was 250ms not triggered');
}, 250);

http://yckart.github.com/jquery.unevent.js/

(การสาธิตนี้ใช้resizeแทนscrollแต่ใครจะสนล่ะ!)


ยังไม่แม่นยำ 100%: บางครั้งผู้ใช้หยุดและเลื่อนต่อแม้จะผ่านไป 250 ms
Arman Bimatov

รหัสนี้ใช้งานได้ดี แต่มันทำให้วิดเจ็ตเติมข้อความอัตโนมัติของ jquery ui พังโดยสิ้นเชิง
kkazakov

@ArmanBimatov จะถือว่าผู้ใช้เลื่อนไปเรื่อย ๆ ซึ่งฟังดูดีใช่ไหม
godblessstrawberry

การหมดเวลานี้จะเริ่มทำงานเฉพาะเมื่อเหตุการณ์การเลื่อนหยุดลงและไม่ใช่เมื่อผู้ใช้หยุดเลื่อน ผู้ใช้สามารถยกนิ้วออกจากเมาส์และการเลื่อนสามารถดำเนินต่อไปได้ 2-3 วินาทีขึ้นอยู่กับความเร็วในการเลื่อน โซลูชันนี้จะไม่ให้สัญญาณบ่งชี้เมื่อผู้ใช้หยุดเลื่อน
AndroidDev

1
@abzarak ผู้ช่วยนามธรรมนี้ไม่สมบูรณ์แบบไม่ว่าในกรณีใด! ฉันไม่ได้อัปเดต github-repo เมื่อเร็ว ๆ นี้ด้วยเหตุผล - นี่เป็นความคิดที่แย่มาก เพียงใช้ฟังก์ชัน wrapper "throttle" หรือ "debounce" แทน ฉันควรสังเกตว่าที่อื่นด้วย! :)
yckart

49

ใช้ jQuery เค้น / debounce

jQuery debounceเป็นวิธีที่ดีสำหรับปัญหาเช่นนี้ jsFidlle

$(window).scroll($.debounce( 250, true, function(){
    $('#scrollMsg').html('SCROLLING!');
}));
$(window).scroll($.debounce( 250, function(){
    $('#scrollMsg').html('DONE!');
}));

พารามิเตอร์ที่สองคือแฟล็ก "at_begin" ที่นี่ฉันได้แสดงวิธีการรันโค้ดทั้งที่ "scroll start" และ "scroll finish"

ใช้ Lodash

ตามคำแนะนำของ Barry P, jsFiddle , underscoreหรือlodashก็มี debounce ซึ่งแต่ละตัวจะมี apis ต่างกันเล็กน้อย

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('SCROLLING!');
}, 150, { 'leading': true, 'trailing': false }));

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));

เป็นไปได้ไหมที่จะใช้ฟังก์ชั่นเลื่อนปกติพร้อมกัน? $ (window) .scroll (ฟังก์ชัน () {... });
Daniel Vogelnest

แน่นอน jQuery จะเชื่อมโยงตัวจัดการกับเหตุการณ์ได้มากเท่าที่คุณต้องการ
Sinetheta

ขอบคุณสำหรับการอัปเดต @BarryP Jsfiddle นี้ยังมี lo-dash เพื่อให้คุณสามารถหลีกเลี่ยงลิงก์ภายนอกjsfiddle.net/qjggnyhf
Sinetheta

FYI ฉันประสบปัญหาที่การเลื่อนอย่างรวดเร็วไม่ย้อนกลับ ดูเหมือนว่าคุณต้องเพิ่มเวลาไม่กี่มิลลิวินาทีในการดีบัก "STOPPED" มิฉะนั้นจะทำให้เกิดสภาวะการแข่งขันที่บางครั้ง STOPPED จะทริกเกอร์ก่อนที่จะเริ่มต้นและคุณจะจบลงด้วยรายการที่ค้างอยู่ราวกับว่าคุณยังคงเลื่อนอยู่ ฉันสร้างของฉัน 150 และ 160 ตามลำดับและดูเหมือนจะทำเคล็ดลับ
CodeChimp

ขอบคุณ @CodeChimp ที่เรียบร้อย แต่ฉันกังวลเกี่ยวกับการจัดการเคสขอบโดยการแก้ไข 15 จาก 16 ครั้ง;) บางทีตัวจัดการเดียวที่มีตรรกะทั้งหมดภายในจะปลอดภัยที่สุด ตรวจสอบleadingและtrailingตัวคุณเองแล้วมั่นใจว่าจะไม่มีความสับสน
Sinetheta

9

Rob W suggected ฉันดูโพสต์อื่นที่นี่ในกองซึ่งโดยพื้นฐานแล้วเป็นโพสต์ที่คล้ายกับโพสต์ต้นฉบับของฉัน ซึ่งเมื่ออ่านแล้วพบลิงก์ไปยังไซต์:

http://james.padolsey.com/javascript/special-scroll-events-for-jquery/

สิ่งนี้จบลงด้วยการช่วยแก้ปัญหาของฉันได้ดีมากหลังจากปรับแต่งความต้องการของตัวเองเล็กน้อย แต่โดยรวมแล้วช่วยให้ได้รับความทุกข์ทรมานมากมายและช่วยฉันประมาณ 4 ชั่วโมงในการคิดออกด้วยตัวเอง

เมื่อเห็นว่าโพสต์นี้ดูเหมือนจะมีประโยชน์ฉันคิดว่าฉันจะกลับมาและให้รหัสที่พบในลิงค์ที่กล่าวถึงในกรณีที่ผู้เขียนเคยตัดสินใจที่จะไปในทิศทางที่แตกต่างกับไซต์และลงเอยด้วยการปิดลิงก์

(function(){

    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1);

    special.scrollstart = {
        setup: function() {

            var timer,
                handler =  function(evt) {

                    var _self = this,
                        _args = arguments;

                    if (timer) {
                        clearTimeout(timer);
                    } else {
                        evt.type = 'scrollstart';
                        jQuery.event.handle.apply(_self, _args);
                    }

                    timer = setTimeout( function(){
                        timer = null;
                    }, special.scrollstop.latency);

                };

            jQuery(this).bind('scroll', handler).data(uid1, handler);

        },
        teardown: function(){
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
        }
    };

    special.scrollstop = {
        latency: 300,
        setup: function() {

            var timer,
                    handler = function(evt) {

                    var _self = this,
                        _args = arguments;

                    if (timer) {
                        clearTimeout(timer);
                    }

                    timer = setTimeout( function(){

                        timer = null;
                        evt.type = 'scrollstop';
                        jQuery.event.handle.apply(_self, _args);

                    }, special.scrollstop.latency);

                };

            jQuery(this).bind('scroll', handler).data(uid2, handler);

        },
        teardown: function() {
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
        }
    };

})();

5

ฉันเห็นด้วยกับความคิดเห็นบางส่วนข้างต้นว่าการฟังการหมดเวลาไม่แม่นยำเพียงพอเนื่องจากจะเกิดขึ้นเมื่อคุณหยุดเลื่อนแถบเลื่อนนานพอแทนที่จะหยุดเลื่อน ฉันคิดว่าทางออกที่ดีกว่าคือฟังผู้ใช้ปล่อยเมาส์ (mouseup) ทันทีที่พวกเขาเริ่มเลื่อน:

$(window).scroll(function(){
    $('#scrollMsg').html('SCROLLING!');
    var stopListener = $(window).mouseup(function(){ // listen to mouse up
        $('#scrollMsg').html('STOPPED SCROLLING!');
        stopListner(); // Stop listening to mouse up after heard for the first time 
    });
});

และตัวอย่างการทำงานสามารถดูได้ในJSFiddle นี้


2
ดูเหมือนจะดี แต่ถ้าคุณเลื่อนด้วยท่าทาง 2 นิ้วบนแทร็คแพดหรือล้อเลื่อนเมาส์อัพจะไม่เริ่มทำงาน นี่อาจเป็นวิธีที่ใช้บ่อยที่สุดในการเลื่อนด้วยซึ่งทำให้เกิดปัญหา
อดัม

1
จุดดี. แต่อาจมีการแก้ไขสองสามอย่างสำหรับสิ่งนั้น ใช้เหตุการณ์ 'mousewheel' ของ jquery หรือติดตามว่ามีการเลื่อนขั้นก่อนหรือไม่และใช้วิธีการหมดเวลาตามที่ผู้อื่นแนะนำ แต่ฉันคิดว่าการใช้คำตอบอื่น ๆ ร่วมกันสำหรับเหตุการณ์ล้อเลื่อนของเมาส์และคำตอบสำหรับการลากแถบเลื่อนนี้จะให้ผลลัพธ์ที่แม่นยำที่สุด
ธีโอ

3

คุณสามารถกำหนดช่วงเวลาที่ทำงานทุกๆ 500 มิลลิวินาทีหรือมากกว่านั้นตามบรรทัดต่อไปนี้:

var curOffset, oldOffset;
oldOffset = $(window).scrollTop();
var $el = $('.slides_layover'); // cache jquery ref
setInterval(function() {
  curOffset = $(window).scrollTop();
  if(curOffset != oldOffset) {
    // they're scrolling, remove your class here if it exists
    if($el.hasClass('showing_layover')) $el.removeClass('showing_layover');
  } else {
    // they've stopped, add the class if it doesn't exist
    if(!$el.hasClass('showing_layover')) $el.addClass('showing_layover');
  }
  oldOffset = curOffset;
}, 500);

ฉันยังไม่ได้ทดสอบโค้ดนี้ แต่หลักการควรใช้งานได้


2
function scrolled() {
    //do by scroll start
    $(this).off('scroll')[0].setTimeout(function(){
        //do by scroll end
        $(this).on('scroll',scrolled);
    }, 500)
}
$(window).on('scroll',scrolled);

เวอร์ชันขนาดเล็กมากพร้อมความสามารถในการเริ่มต้นและสิ้นสุด


1

ตกลงนี่คือสิ่งที่ฉันเคยใช้มาก่อน scrollTop()โดยทั่วไปคุณดูการระงับการเตะไปยังหน้าล่าสุด เมื่อหมดเวลาล้างคุณจะตรวจสอบกระแสscrollTop()และหากเหมือนกันแสดงว่าคุณเลื่อนเสร็จแล้ว

$(window).scroll((e) ->
  clearTimeout(scrollTimer)
  $('header').addClass('hidden')

  scrollTimer = setTimeout((() ->
    if $(this).scrollTop() is currentScrollTop
      $('header').removeClass('hidden') 
  ), animationDuration)

  currentScrollTop = $(this).scrollTop()
)

1

สไตล์ ES6 พร้อมการตรวจสอบการเลื่อนเริ่มต้นด้วย

function onScrollHandler(params: {
  onStart: () => void,
  onStop: () => void,
  timeout: number
}) {
  const {onStart, onStop, timeout = 200} = params
  let timer = null

  return (event) => {
    if (timer) {
      clearTimeout(timer)
    } else {
      onStart && onStart(event)
    }
    timer = setTimeout(() => {
      timer = null
      onStop && onStop(event)
    }, timeout)
  }
}

การใช้งาน:

yourScrollableElement.addEventListener('scroll', onScrollHandler({
  onStart: (event) => {
    console.log('Scrolling has started')
  },
  onStop: (event) => {
    console.log('Scrolling has stopped')
  },
  timeout: 123 // Remove to use default value
}))


0

สำหรับผู้ที่ยังต้องการนี่คือทางออก

  $(function(){
      var t;
      document.addEventListener('scroll',function(e){
          clearTimeout(t);
          checkScroll();
      });
      
      function checkScroll(){
          t = setTimeout(function(){
             alert('Done Scrolling');
          },500); /* You can increase or reduse timer */
      }
  });


0

สิ่งนี้ควรใช้งานได้:

var Timer;
$('.Scroll_Table_Div').on("scroll",function() 
{
    // do somethings

    clearTimeout(Timer);
    Timer = setTimeout(function()
    {
        console.log('scrolling is stop');
    },50);
});

0

นี่คือวิธีที่คุณสามารถจัดการได้:

    var scrollStop = function (callback) {
        if (!callback || typeof callback !== 'function') return;
        var isScrolling;
        window.addEventListener('scroll', function (event) {
            window.clearTimeout(isScrolling);
            isScrolling = setTimeout(function() {
                callback();
            }, 66);
        }, false);
    };
    scrollStop(function () {
        console.log('Scrolling has stopped.');
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>
</body>
</html>


0

สิ่งนี้ตรวจพบการหยุดการเลื่อนหลังจากผ่านไป 1 มิลลิวินาที (หรือเปลี่ยนแปลง) โดยใช้ตัวจับเวลาส่วนกลาง:

var scrollTimer;

$(window).on("scroll",function(){
    clearTimeout(scrollTimer);
    //Do  what you want whilst scrolling
    scrollTimer=setTimeout(function(){afterScroll()},1);
})

function afterScroll(){
    //I catched scroll stop.
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.