2

I want a function to run almost instantaneously as soon as the user stops scrolling, without waiting for inertia scrolling to finish. I have checked several answers like this one: force-stop momentum scrolling on iphone/ipad in javascript and this: Disable inertia scroll for "single-page" webapp, but I need to use the wheel event since mousewheel is deprecated and e.preventdefault() doesn't answer my question. This answer: How do I know when I've stopped scrolling? also doesn't consider inertia scrolling and the event fires a few seconds after the user has stopped scrolling.

kolman
  • 330
  • 1
  • 3
  • 12

1 Answers1

1

The approach I came up with suits my needs and I couldnt find any other solution on the net so I'll answer this question Q&A style, if a better approach is used to answer this question, ill be more than happy to mark that as the answer until then, ill keep this open for a few days.

I was testing it out and I saw event.deltaY to increase till a max value and when the user stops scrolling, it starts decreasing BUT thats not always the case, since from my understanding deltaY is the change in Y from the last event was fired, so a code like this

 mainElement!.addEventListener("wheel", (ev) => {
        ev.preventDefault(); // stop scrolling
        const absEvVal = Math.abs(ev.deltaY);
        console.log(ev.deltaY);
        if (absEvVal > max_abs_ev_val) {
            max_abs_ev_val = absEvVal;
            switchy = true;
        } else {
            if (switchy) {
                console.log("max val", max_abs_ev_val);
                console.log("do function now");
                switchy = false;
            }
        }
})

can produce inconsistent results like this (albeit rarely) showing result of above code ^ Here I only scrolled once.

So I combined the timeout function with my code and it works perfectly,


    let timerTimeout: any;
    let switchy = true;
    let performLogic = true;
    let abslastDeltaY = 0;


    mainElement!.addEventListener("wheel", (ev) => {
        ev.preventDefault(); // stop scrolling
        const absEvVal = Math.abs(ev.deltaY);
        if (absEvVal > 100) { // a threshold value, just in case the user accidentally scrolls a bit, dont want to run the code needlessly
            if (absEvVal > abslastDeltaY) {
                switchy = true;
            } else if (absEvVal < abslastDeltaY && switchy) { //this code will run just before the abs(ev.deltaY) starts decreasing
    
                clearTimeout(timerTimeout);
                if (performLogic) {
                    console.log("do logic");
                    performLogic = false;
                }

                timerTimeout = setTimeout(() => {
                    switchy = false;
                    performLogic = true;
                }, 100);
            }
            abslastDeltaY = Math.abs(ev.deltaY);
        }
    })

Now in my code here, note that I am performing the logic BEFORE setTimeout, this would mean that my code will run before the user has finished scrolling , in those rare cases where the first code fails, but this will be very close to when the user finishes scrolling so its alright for me. If you dont mind a bit of delay (the one you provide to your timeout) , then you can put the code in the timeout rather than before it , like so

  ...
                clearTimeout(timerTimeout);
                timerTimeout = setTimeout(() => {
                     if (performLogic) {
                         console.log("do logic");
                         performLogic = false;
                    }
                    switchy = false;
                    performLogic = true;
                }, 100);
    ...

I've tested this on Chrome and Safari and it works properly.

kolman
  • 330
  • 1
  • 3
  • 12