jquery mousewheel: detecting when the wheel stops?
Asked Answered
C

5

24

I'm using Jquery mousewheel plugin and I would like like to be able to detect when the user has finished using the wheel. Similar functionality as the stop: event in the draggable stuff. Can somebody point me to the right direction?

Crosspollinate answered 18/8, 2010 at 18:35 Comment(0)
H
44

There's no "stop" event here really - you get an event when you do scroll, so every time a mousewheel event happens the event is triggered...when there's nothing you'll get no events and your handler won't be firing.

You ca however detect when the user hasn't used it in say 250ms, like this:

$("#myElem").mousewheel(function() {
  clearTimeout($.data(this, 'timer'));
  $.data(this, 'timer', setTimeout(function() {
     alert("Haven't scrolled in 250ms!");
     //do something
  }, 250));
});

You can give it a try here, all we're doing is storing the timeout on each use in using $.data(), if you use it again before that time runs out, it gets cleared...if not then whatever code you wanted to run fires, the user has "finished" using their mousewheel for whatever period of time you're testing for.

Hildie answered 18/8, 2010 at 18:40 Comment(3)
Yeah, I was thinking about something similar. I will give it a try. Thanks a lot Nick.Crosspollinate
@Crosspollinate - Welcome :) If it works out be sure to accept answers on your questions ;)Hildie
Not working for kinetic scrolling like the ones in Apple laptops or the Magic mouse.Firenew
I
12

To complete Nick Craver's answer:

var wheeldelta = {
  x: 0,
  y: 0
};
var wheeling;
$('#foo').on('mousewheel', function (e) {
  if (!wheeling) {
    console.log('start wheeling!');
  }

  clearTimeout(wheeling);
  wheeling = setTimeout(function() {
    console.log('stop wheeling!');
    wheeling = undefined;

    // reset wheeldelta
    wheeldelta.x = 0;
    wheeldelta.y = 0;
  }, 250);

  wheeldelta.x += e.deltaFactor * e.deltaX;
  wheeldelta.y += e.deltaFactor * e.deltaY;
  console.log(wheeldelta);
});

scrolling outputs:

start wheeling!
Object {x: -1, y: 0}
Object {x: -36, y: 12}
Object {x: -45, y: 12}
Object {x: -63, y: 12}
Object {x: -72, y: 12}
Object {x: -80, y: 12}
Object {x: -89, y: 12}
Object {x: -97, y: 12}
Object {x: -104, y: 12}
Object {x: -111, y: 12}
Object {x: -117, y: 12}
Object {x: -122, y: 12}
Object {x: -127, y: 12}
Object {x: -131, y: 12}
Object {x: -135, y: 12}
Object {x: -139, y: 12}
Object {x: -145, y: 12}
Object {x: -148, y: 12}
Object {x: -152, y: 12}
Object {x: -154, y: 12}
Object {x: -156, y: 12}
Object {x: -157, y: 12}
Object {x: -158, y: 12}
Object {x: -159, y: 12}
Object {x: -161, y: 12}
Object {x: -162, y: 12}
Object {x: -164, y: 12}
Object {x: -166, y: 12}
Object {x: -167, y: 12}
Object {x: -169, y: 12}
stop wheeling!
Ingratiating answered 6/2, 2015 at 17:9 Comment(0)
K
4

Here's how to do it in native JavaScript:

var _scrollTimeout = null;

function onMouseWheel() {
    var d = ((typeof e.wheelDelta != "undefined") ? (-e.wheelDelta) : e.detail);
    d = 100 * ((d>0)?1:-1);

    console.log("Scroll delta", d);

    clearTimeout(_scrollTimeout);
    _scrollTimeout = setTimeout(function() {
        console.log("Haven't scrolled in 250ms");
    }, 250);
}

window.addEventListener( 'mousewheel', onMouseWheel, false );
window.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
Kelcey answered 21/6, 2016 at 17:39 Comment(0)
M
2

Here's how to implement your own wheel stop event.

//initialise the new variables
var wheelMap = new Map;
var deltaXEnded = false;
var deltaYEnded = false;
var previousSwipe = Object;
    previousSwipe.timeStamp = 0;
    previousSwipe.deltaX = 0;
    previousSwipe.deltaY = 0;
var wheelstart = false;

make a new eventListener for the wheelstop event

which we will be calling from the normalWheelEventCallbackFunction()

var wheelstop = new Event("wheelstop");

next we will define the callback in the case of this event being dispatched & then add the event to the window object.

function wheelstopcallback(event){
    wheelstart = false;
    console.log("wheel event has ended");
}
window.addEventListener("wheelstop", wheelstopcallback.bind(this));

now we define the normal wheel event listener as well as define the callback this listener will be using...

window.addEventListener("wheel", normalWheelEventCallbackFunction.bind(this));

The Wheel Event Callback Function

function normalWheelEventCallbackFunction(event){
   if(previousSwipe.timeStamp !== 0){
      if(event.timeStamp - previousSwipe.timeStamp < 1000)
         wheelMap.set(event.timeStamp, event);
      else
         wheelMap.clear();
   }
 else{previousSwipe.timeStamp = event.timeStamp;}


  if(event.deltaX > 2 && event.deltaX > previousSwipe.deltaX){
     //forward
     wheelstart = true
  }
  else if(event.deltaX < -2&& event.deltaX < previousSwipe.deltaX){
     //backward
     wheelstart = true;
  }
  else if(event.deltaY > 2 && event.deltaY > previousSwipe.deltaY){
     wheelstart = true;
  }
  else if(event.deltaY < 2 && event.deltaY < previousSwipe.deltaY){
     wheelstart = true;
  }
  
  if(
     ((event.deltaX === 1 || event.deltaX === 0 || event.deltaX === -1) && ((event.deltaX > 0 && event.deltaX < previousSwipe.deltaX) || (event.deltaX < 0 && event.deltaX > previousSwipe.deltaX)) && wheelstart)
     || (wheelstart && (event.deltaX === 0 && previousSwipe.deltaX === 0))
  )
  {
     deltaXEnded = true;
     console.log("deltaXEnded");
  }
  if(
     (((event.deltaY === 1 || event.deltaY === 0 || event.deltaY === -1) && ((event.deltaY > 0 && event.deltaY < previousSwipe.deltaY) || (event.deltaY < 0 && event.deltaY > previousSwipe.deltaY))) && wheelstart)
     || (wheelstart && (event.deltaY === 0 && previousSwipe.deltaY === 0)))     {
        deltaYEnded = true;
        console.log("deltaYEnded");
     }
  
     if(deltaXEnded && deltaYEnded){
        deltaXEnded = false;
        deltaYEnded = false;
        window.dispatchEvent(wheelstop);
     }

  previousSwipe.deltaX = event.deltaX;
  previousSwipe.deltaY = event.deltaY;
}

this may have a few mistakes, but for the most part the logic is pretty sound, I would recommend a fallback however if you are required to catch every single wheel event dispatched since it may have some after the 'wheelstop' event has been dispatched.

oh and lastly be sure and implement a handler for if it is interrupted by the click event which consequently ends the wheel event...

function wheelstopdispatch(){
  if(wheelstart)
    window.dispatchEvent(wheelstop);
  }
window.addEventListener("click", wheelstopdispatch);
Messiah answered 7/8, 2017 at 18:8 Comment(0)
M
1

Nick Craver's answer works fine. But it will cause a small delay (of 250ms) to perform the // do something. A better option would be to execute your codes right away and wait delayms before catching further events.

To do this, use a global variable say processing, initialize it with false and toggle it's value before and after code execution.

window.processing = false;
$("#myElem").mousewheel(function() {
   if (processing === false) {
     processing = true;
     // do something
     setTimeout(function() {
       processing = false;
     }, 250)); // waiting 250ms to change back to false.
   }
 });
Mahone answered 1/5, 2018 at 7:1 Comment(1)
This doesn't really answer the question, nor does it provide a good (or "better") alternative to Tim's. For example, if you want to show a modal after the user stopped scrolling, this answer will make the modal appear immediately after the first event. Keep in mind that unlike keyboard or mouse events, wheel events come in batches, because for example, users need to move back the finger to the top of the wheel to continue scrolling. This approach you're proposing makes sense for throttling...not what the OP asked.Egret

© 2022 - 2024 — McMap. All rights reserved.