jQuery scroll () detecta quando o usuário para de rolar

109

Ok com isso ..

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

Posso dizer quando alguém está rolando pelo que entendi. Então, estou tentando descobrir como pegar quando alguém parou. No exemplo acima, você pode ver que estou removendo uma classe de um conjunto de elementos enquanto a rolagem está ocorrendo. No entanto, quero colocar essa classe de volta quando o usuário parar de rolar.

O motivo para isso é que pretendo fazer um show de espera enquanto a página rola para dar à página um efeito especial que estou tentando trabalhar. Mas a única classe que estou tentando remover durante a rolagem está em conflito com esse efeito, pois é um efeito de transparência para alguma natureza.

chris
fonte
1
possível duplicação do evento
Rob W
Incrível, não exatamente duplicado, mas definitivamente resolvido o que eu estava procurando e me ajudou no final a resolver meu problema. Obrigado.
Chris

Respostas:

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

Atualizar

Eu escrevi uma extensão para aprimorar o onmanipulador de eventos padrão do jQuery . Ele anexa uma função de manipulador de eventos para um ou mais eventos aos elementos selecionados e chama a função de manipulador se o evento não foi disparado por um determinado intervalo. Isso é útil se você deseja disparar um retorno de chamada somente após um atraso, como o evento de redimensionamento ou algo semelhante.

É importante verificar o github-repo para atualizações!

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

Use-o como qualquer outro gerenciador de evento onou bind, exceto que você pode passar um parâmetro extra como último:

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

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

(esta demonstração usa em resizevez de scroll, mas quem se importa ?!)

Yckart
fonte
Ainda não é 100% preciso: às vezes o usuário para e retoma a rolagem mesmo após 250 ms
Arman Bimatov
Este código funciona muito bem, mas quebrou totalmente o widget de preenchimento automático do jquery ui.
kkazakov
@ArmanBimatov então será considerado conforme o usuário continua rolando, o que parece bom, não?
godblessstrawberry
Este tempo limite só dispara quando os eventos de rolagem param e NÃO quando o usuário para de rolagem. O usuário pode tirar o dedo do mouse e a rolagem pode continuar por alguns segundos, dependendo da velocidade de rolagem. Esta solução não fornecerá uma indicação de quando o usuário parou de rolar.
AndroidDev
1
@abzarak este auxiliar abstrato não é perfeito, em nenhum caso! Não atualizei o github-repo recentemente, por motivos - foi uma ideia terrível. Basta usar uma função de wrapper "throttle" ou "debounce". Devo notar isso em outro lugar também! :)
yckart
49

Usando jQuery throttle / debounce

jQuery debounce é bom para problemas como este. jsFidlle

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

O segundo parâmetro é o sinalizador "at_begin". Aqui, mostrei como executar código tanto no "início da rolagem" quanto no "final da rolagem".

Usando Lodash

Como sugerido por Barry P, jsFiddle , underscore ou lodash também têm um debounce, cada um com apis ligeiramente diferente.

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

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));
Sinetheta
fonte
É possível utilizar uma função de rolagem normal ao mesmo tempo? $ (janela) .scroll (função () {...});
Daniel Vogelnest
Obviamente, o jQuery vinculará quantos manipuladores a um evento você desejar.
Sinetheta
Obrigado por atualizar este @BarryP Jsfiddle também fornece lo-dash para que você possa evitar o link externo jsfiddle.net/qjggnyhf
Sinetheta
Para sua informação, eu estava tendo problemas em que os pergaminhos rápidos não estavam voltando. Pareceu que você precisava adicionar alguns milissegundos ao debounce "STOPPED", senão isso causa uma condição de corrida onde, em algum momento, STOPPED dispara antes do STARTTED, e você acaba com o item preso como se ainda estivesse rolando. Fiz os meus 150 e 160, respectivamente, e parecia funcionar.
CodeChimp
Obrigado @CodeChimp, isso é legal, mas eu estava preocupado em lidar com casos extremos corrigindo-os 15 de 16 vezes;) Talvez um único manipulador com toda a lógica interna fosse o mais seguro. Verifique você mesmo leadinge trailing, em seguida, certifique-se de que não pode haver confusão.
Sinetheta
9

Rob W sugeriu que eu desse uma olhada em outra postagem aqui na pilha que era essencialmente uma postagem semelhante à minha original. Lendo isso, encontrei um link para um site:

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

Na verdade, isso acabou ajudando a resolver meu problema muito bem depois de alguns ajustes para minhas próprias necessidades, mas, acima de tudo, ajudou a tirar muitas gargalhadas do caminho e me poupou cerca de 4 horas para descobrir por conta própria.

Vendo que esta postagem parece ter algum mérito, resolvi voltar e fornecer o código encontrado originalmente no link mencionado, caso o autor alguma vez decidisse seguir uma direção diferente com o site e acabasse retirando o link.

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

})();
chris
fonte
5

Concordei com alguns dos comentários acima que ouvir um tempo limite não foi preciso o suficiente, pois isso será acionado quando você parar de mover a barra de rolagem por tempo suficiente, em vez de quando você parar de rolar. Acho que a melhor solução é ouvir o usuário soltando o mouse (mouseup) assim que começa a rolar:

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

e um exemplo de como ele funciona pode ser visto neste JSFiddle

Theo
fonte
2
Isso parece ótimo, mas se você estiver rolando com um gesto de 2 dedos em um trackpad ou uma roda de rolagem, o mouseup não será disparado. Essa é provavelmente a maneira mais comum de rolar também, o que a torna problemática.
Adam
1
Bom ponto. Mas, potencialmente, existem algumas soluções para isso. Usando o evento 'mousewheel' do jquery ou controlando se o mouse foi abaixado primeiro, e usando uma abordagem de tempo limite sugerido por outros. Mas acho que usar uma combinação de outras respostas para eventos da roda do mouse e esta resposta para arrastar a barra de rolagem dará os resultados mais precisos
Theo,
3

Você pode definir um intervalo que é executado a cada 500 ms ou mais, nas linhas do seguinte:

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

Não testei este código, mas o princípio deve funcionar.

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

Versão muito pequena com capacidade de início e fim

Dieter Bender
fonte
1

Ok, isso é algo que eu usei antes. Basicamente, você olha um árbitro até o fim scrollTop(). Assim que o tempo limite for apagado, você verifica os atuais scrollTop()e, se eles são iguais, a rolagem é concluída.

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

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

  currentScrollTop = $(this).scrollTop()
)
matrangam
fonte
1

Estilo ES6 com verificação de início de rolagem também.

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

Uso:

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
}))
Ilya Iksent
fonte
0

por favor, verifique o evento jquery mobile scrollstop

$(document).on("scrollstop",function(){
  alert("Stopped scrolling!");
});
Irshad Pathan
fonte
3
isso é jquery mobile, não jquery. quase caí na mesma armadilha;)
katzenhut
0

Para quem ainda precisa, aqui está a solução

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

smacaz
fonte
0

Isso deve funcionar:

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

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

Veja como você pode lidar com isso:

    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>

Mohammad Ghonchesefidi
fonte
0

Isso detecta a parada de rolagem após 1 milissegundo (ou altere-o) usando um cronômetro global:

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.
}
Ali Sheikhpour
fonte