How can I differentiate a manual scroll (via mousewheel/scrollbar) from a Javascript/jQuery scroll?

16,220

Solution 1

Try this function:

$('body,html').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(e){
 if ( e.which > 0 || e.type == "mousedown" || e.type == "mousewheel"){
  $("html,body").stop();
 }
})

Also, did you see this tutorial?

Update: Modern browsers now use "wheel" as the event, so I've included it in the code above.

Solution 2

I had your same issue some a few days ago.You shouldn't be using jquery's animate function if you want to obtain that result, you have to simulate the animation using a polling function.

I made this class which is supposed to provide a smooth scrolldown when ScrollDown.slow() is called.

ScrollDown.current=$(window).scrollTop();
ScrollDown.lastValue;
ScrollDown.lastType;
ScrollDown.enabled=true;
ScrollDown.custom=function(value,rate){  //let's say value==='bottom' and rate=10
    if(value==='bottom'){
        value=$(document).height()-$(window).height();
    }
    ScrollDown.current=$(window).scrollTop();
    ScrollDown.lastValue=value;
    (function poll(){
        setTimeout(function(){
            var prev=$(window).scrollTop();  //This is the critical part
            /*I'm saving again the scroll position of the window, remember
            10 ms have passed since the polling has started
            At this rate, if the user will scroll up for down pre!==ScrollDown.current
            And that means I have to stop scrolling.*/
            ScrollDown.current++; //increasing the scroll variable so that it keeps scrolling
            $(window).scrollTop(ScrollDown.current);
            if(ScrollDown.current<ScrollDown.lastValue && ScrollDown.enabled){  
            //ScrollDown.current<ScrollDown.lastValue basically checks if it's reached the bottom
                if(prev!==ScrollDown.current-1){
                /*I'm checking if the user 
                scrolled up or down while the polling has been going on, 
                if the user scrolls up then prev<ScrollDown.current-1, 
                if the user scrolls down then prev>ScrollDown.current-1 
                and at the next poll() the scrolling will stop 
                because ScrollDown.enabled will bet set to false by ScrollDown.stop()*/
                    ScrollDown.stop();
                }
                poll();
            }
        },rate);
    })();
};

ScrollDown.stop=function(){
    ScrollDown.enabled=false;
};

ScrollDown.continue=function(){
    ScrollDown.enabled=true;
    switch (ScrollDown.lastType){
        case "fast":
            ScrollDown.fast(ScrollDown.lastValue);
            break;
        case "normal":
            ScrollDown.normal(ScrollDown.lastValue);
            break;
        case "slow":
            ScrollDown.slow(ScrollDown.lastValue);
            break;
    }
};

ScrollDown.fast=function(value){
    if(!ScrollDown.enabled){
        ScrollDown.continue();
    }else{
        ScrollDown.lastType='fast';
        ScrollDown.custom(value,1);
    }
};
ScrollDown.normal=function(value){
    if(!ScrollDown.enabled){
        ScrollDown.continue();
    }else{
        ScrollDown.lastType='normal';
        ScrollDown.custom(value,10);
    }
};
ScrollDown.slow=function(value){
    if(!ScrollDown.enabled){
        ScrollDown.continue();
    }else{
        ScrollDown.lastType='slow';
        ScrollDown.custom(value,50);
    }
};
function ScrollDown(){}

So if you were to call ScrollDown.slow('bottom') it would start scrolling slowly till it reaches the bottom of your page unless you scroll up or down manually, then it stops.

Share:
16,220

Related videos on Youtube

David Murdoch
Author by

David Murdoch

I'm an Ethereum enthusiast and Software Development Lead at Truffle. I'm the core contributor to Ganache. I'm a husband, father of 2, have 6 siblings, enjoying traveling, and can solve a Rubik's Cube in under 2 minutes. I play bass, guitar, piano, and a little drums. I'm a terrible singer. I drive a 90's M3 because I enjoy fixing things.

Updated on January 05, 2020

Comments

  • David Murdoch
    David Murdoch over 4 years

    UPDATE:

    Here is a jsbin example demonstrating the problem.

    UPDATE 2:
    And here is the fixed version thanks to fudgey.


    Basically, I have the following javascript which scrolls the window to an anchor on the page:

     // get anchors with href's that start with "#"
     $("a[href^=#]").live("click", function(){
         var target = $($(this).attr("href"));
         // if the target exists: scroll to it...
         if(target[0]){
             // If the page isn't long enough to scroll to the target's position
             // we want to scroll as much as we can. This part prevents a sudden 
             // stop when window.scrollTop reaches its maximum.
             var y = Math.min(target.offset().top, $(document).height() - $(window).height());
             // also, don't try to scroll to a negative value...
             y=Math.max(y,0);
             // OK, you can scroll now...
             $("html,body").stop().animate({ "scrollTop": y }, 1000);
         }
         return false;
     });
    

    It works perfectly......until I manually try to scroll the window. When the scrollbar or mousewheel is scrolled I need to stop the current scroll animation...but I'm not sure how to do this.

    This is probably my starting point...

    $(window).scroll(e){
        if(IsManuallyScrolled(e)){
            $("html,body").stop();
        }
    } 
    

    ...but I'm not sure how to code the IsManuallyScrolled function. I've checked out e (the event object) in Google Chrome's console and AFAIK there is not way to differentiate between a manual scroll and jQuery's animate() scroll.

    How can I differentiate between a manual scroll and one called via jQuery's $.fn.animate function?

  • David Murdoch
    David Murdoch almost 14 years
    THANK YOU! No, I didn't see the tutorial, thanks for that too. Here is an updated JSBin example with your fix
  • David Murdoch
    David Murdoch almost 14 years
    Oh, and why use DOMMouseScroll? Does "scroll" !== "DomMouseScroll"?
  • David Murdoch
    David Murdoch almost 14 years
    This won't work because window.IsAutoScrolling will always be true until the scrolling stops...at which point we no longer care that the window is auto scrolling. The problem is detecting what is calling the scroll DURING the animate scroll; jquery or the user.
  • Mottie
    Mottie almost 14 years
    DOMMouseScroll is for Firefox only (ref: quirksmode.org/dom/events/scroll.html)
  • David Murdoch
    David Murdoch almost 14 years
    So jQuery doesn't map DOMMouseScroll to scroll for us?
  • Mottie
    Mottie almost 14 years
    Oddly enough, no, so to support it you'll need to use the plugin (plugins.jquery.com/project/mousewheel)
  • Mottie
    Mottie almost 14 years
    Just in case you are interested, I used some of your code to help someone with this question (stackoverflow.com/questions/2896869/…)... here is that demo (jsfiddle.net/m2zQE) :) Cheers!
  • Mottie
    Mottie about 11 years
    Update: added "wheel" event to the binding.
  • Ben
    Ben over 9 years
    For touch screen devices you will need to add touchstart to your list of events, and check for it in the event itself ... || e.type == 'touchstart'
  • 1GR3
    1GR3 over 6 years
    Ironically, modern computers now rarely use wheels :)
  • Mottie
    Mottie over 6 years
    @1GR3 Then, I'd call that a tablet; not a computer ;)
  • 1GR3
    1GR3 over 6 years
    I'd say that 27" is too big for a tablet ;)
  • Ricky Boyce
    Ricky Boyce almost 5 years
    I'd say using a 27" touch screen is crazy ;)