16

This question was already asked here a long time ago:

Detect jquery event trigger by user or call by code

But it has never been answered conclusively (or maybe I'm simply not able to search properly).

Is it possible to detect whether a scroll event has been triggered by the user or by the jQuery animate function?

I am trying to prevent the scroll event to trigger itself while doing something like this:

$(document).scroll(function(){
    $("html").stop(true);
    var number = 400; //some other stuff is happening here
    clearTimeout(tout);
    tout = setTimeout(function(){
        if(top == $(document).scrollTop()){
            $("html").animate({
                scrollTop: (number),
                easing: "easeInQuad",
                duration: 110
            });
        }
    },120);
});

This code seems to be suitable:

$('#scroller').scroll(function(e) {
    if (e.originalEvent) {
        console.log('scroll happen manual scroll');
    } else {
        console.log('scroll happen by call');
    }
});

But the originalEvent object isn't able to detect the animate trigger properly.

Is there any other way to do this?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ramon
  • 424
  • 2
  • 9
  • 24

7 Answers7

24

Maybe :animated selector will help you:

$('#scroller').scroll(function(e) {
    if ($(this).is(':animated')) {
        console.log('scroll happen by animate');
    } else if (e.originalEvent) {
        // scroll happen manual scroll
        console.log('scroll happen manual scroll');
    } else {
        // scroll happen by call
        console.log('scroll happen by call');
    }
});

Demo

Tony
  • 7,345
  • 3
  • 26
  • 34
  • 1
    demo url is now dead =( – Capagris Sep 18 '15 at 03:30
  • 4
    This doesn't work as expected because jquery changes `.is(':animated')` to false THEN scrolls 1 more pixel. That 1px of scroll triggers the scroll event but this time with `.is(':animated')` set to false which makes `.is(':animated')` almost completely useless for this use case unless you can find a way around the bug. – Daniel Tonon Mar 31 '16 at 01:11
  • 1
    @DanielTonon please see my solution below to the issue you brought up: http://stackoverflow.com/a/39606080/575594 – cwal Sep 21 '16 at 01:20
  • 1
    Using jQuery 1.12.3, `e.originalEvent` is always there. I mean for manual trigger or not (not tested for animation). Though, to be precise, the scroll event is called because after an animation (on callback) I set back the original scroll value. – Master DJon Oct 27 '16 at 20:12
  • 1
    Two wrong statements in one answer. This answer should have -1 – Federico Schiocchet Nov 29 '21 at 16:09
10

I don't know how well this works with touch screen devices but this works for me on desktop at least

$(window).on('mousewheel', function(){
    //code that will only fire on manual scroll input
});

$(window).scroll(function(){
    //code that will fire on both mouse scroll and code based scroll
});

I don't think there is a way to only target the animated scroll (the accepted answer didn't work for me).

UPDATE: Warning!

Unfortunately, 'mousewheel' doesn't seem to pick up on users who manually grab the scroll bar and drag it or users who use the scroll bar arrow buttons :(

This still works ok for touch screen devices as their swipes seem to count as mouse scrolls. This isn't a great solution for desktop users though.

Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64
3

Using @Tony's accepted answer and @DanielTonon's comment I came up with the following solution:

  var animatedScroll = false;
  var lastAnimatedScroll = false;
  $(window).scroll(function(event){
    lastAnimatedScroll = animatedScroll;
    animatedScroll = $('html, body').is(':animated');
  });

This seems to solve the issue mentioned whereby jquery removes the .is(':animated') then scrolls one more pixel, which leads to .is(':animated') ending on a false. By storing the second to last version of .is(':animated') you can be (more) sure whether or not the scroll was an animated one or not.

When you want to know if the scroll was animated or not just check the lastAnimatedScroll variable.

This has NOT been thoroughly tested by me but has been correct on many page refreshes so I will assume it works well enough.

cwal
  • 3,732
  • 3
  • 19
  • 20
3

After attempting to implement the various solutions in this issue I came up with a different approach that is working well for me.

I use a manual boolean for whether an animation is running:

var isRunningAnimation = false;

and set it to true just before animating, and false in the jQuery animate callback function:

  isRunningAnimation = true;

  $('html').animate({
    scrollLeft: 100,
    scrollTop:  100
  }, 400, 'swing', function() {
    isRunningAnimation = false;
  });

and then in the scroll listener just check that boolean:

$('scroll', function() {
  if (!isRunningAnimation) {
    // If we made it to here, the animation isn't running
  }
});

Of course technically if the user decides to manually scroll during the animation, that won't trigger the on scroll logic either, but that seems like enough of an edge case to not worry about.

SubJunk
  • 150
  • 8
2

I would suggest First of all create a javascript function

// Attaching scroll event when document/window is loaded
    function OnFirstLoad() {
        if (document.attachEvent) {
            document.attachEvent('onscroll', scrollEvent);
        } else if (document.addEventListener) {
            document.addEventListener('scroll', scrollEvent, false);
        }

    }

then, use either

        window.onload = OnFirstLoad;

Or

    $(document).ready(function () {
         OnFirstLoad();
    });

In This scroll event is a function

function scrollEvent(e) {
        var body = document.body,
             html = document.documentElement;

        var docHeight = Math.max(body.scrollHeight, body.offsetHeight,
                               html.clientHeight, html.scrollHeight, html.offsetHeight);
        var currentScroll = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
        // implement your logic according to requirement

    }
Hitesh Gaur
  • 362
  • 1
  • 11
1

If you want to bind with jquery selector and check for event

$('#div.class').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove', function (e) {
    if (e.which > 0 || e.type == "mousedown" || e.type == "mousewheel" || e.type == "touchmove") {
      // any code
    }
})
VipinKundal
  • 442
  • 6
  • 14
1
jQuery(document).on('click', 'p.questions__text a[data-clickid="delay_next_delivery"]', function(ele){
    if(ele.originalEvent.isTrusted){
        // human
    } else {
        // non human
    }
});