Normalizing mousewheel speed across browsers

javascript, mousewheel

Solution

Edit September 2014

Given that:

- Different versions of the same browser on OS X have yielded different values in the past, and may do so in the future, and that

- Using the trackpad on OS X yields very similar effects to using a mouse wheel, yet gives very different event values, and yet the device difference cannot be detected by JS

…I can only recommend using this simple, sign-based-counting code:

var handleScroll = function(evt){
  if (!evt) evt = event;
  var direction = (evt.detail<0 || evt.wheelDelta>0) ? 1 : -1;
  // Use the value as you will
};
someEl.addEventListener('DOMMouseScroll',handleScroll,false); // for Firefox
someEl.addEventListener('mousewheel',    handleScroll,false); // for everyone else

Original attempt to be correct follows.

Here is my first attempt at a script to normalize the values. It has two flaws on OS X: Firefox on OS X will produce values 1/3 what they should be, and Chrome on OS X will produce values 1/40 what they should be.

// Returns +1 for a single wheel roll 'up', -1 for a single roll 'down'
var wheelDistance = function(evt){
  if (!evt) evt = event;
  var w=evt.wheelDelta, d=evt.detail;
  if (d){
    if (w) return w/d/40*d>0?1:-1; // Opera
    else return -d/3;              // Firefox;         TODO: do not /3 for OS X
  } else return w/120;             // IE/Safari/Chrome TODO: /3 for Chrome OS X
};

You can test out this code on your own browser here: http://phrogz.net/JS/wheeldelta.html

Suggestions for detecting and improving the behavior on Firefox and Chrome on OS X are welcome.

Edit: One suggestion from @Tom is to simply count each event call as a single move, using the sign of the distance to adjust it. This will not give great results under smooth/accelerated scrolling on OS X, nor handle perfectly cases when the mouse wheel is moved very fast (e.g. `wheelDelta` is 240), but these happen infrequently. This code is now the recommended technique shown at the top of this answer, for the reasons described there.

Problem

For a different question I composed this answer, including this sample code. In that code I use the mouse wheel to zoom in/out of an HTML5 Canvas. I found some code that normalizes speed differences between Chrome and Firefox. However, the zoom handling in Safari is much, much faster than in either of those. Here's the code I currently have: ``` var handleScroll = function(e){ var delta = e.wheelDelta ? e.wheelDelta/40 : e.detail ? -e.detail/3 : 0; if (delta) ... return e.preventDefault() && false; }; canvas.addEventListener('DOMMouseScroll',handleScroll,false); // For Firefox canvas.addEventListener('mousewheel',handleScroll,false); // Everyone else ``` What code can I use to get the same 'delta' value for the same amount of mouse wheel rolling across Chrome v10/11, Firefox v4, Safari v5, Opera v11 and IE9? This question is related, but has no good answer. Edit: Further investigation shows that one scroll event 'up' is: ``` | evt.wheelDelta | evt.detail ------------------+----------------+------------ Safari v5/Win7 | 120 | 0 Safari v5/OS X | 120 | 0 Safari v7/OS X | 12 | 0 Chrome v11/Win7 | 120 | 0 Chrome v37/Win7 | 120 | 0 Chrome v11/OS X | 3 (!) | 0 (possibly wrong) Chrome v37/OS X | 120 | 0 IE9/Win7 | 120 | undefined Opera v11/OS X | 40 | -1 Opera v24/OS X | 120 | 0 Opera v11/Win7 | 120 | -3 Firefox v4/Win7 | undefined | -3 Firefox v4/OS X | undefined | -1 Firefox v30/OS X | undefined | -1 ``` Further, using the MacBook trackpad on OS X gives different results even when moving slowly: - On Safari and Chrome, the `wheelDelta` is a value of 3 instead of 120 for mouse wheel. - On Firefox the `detail` is usually `2`, sometimes `1`, but when scrolling very slowly NO EVENT HANDLER FIRES AT ALL. So the question is: What is the best way to differentiate this behavior (ideally without any user agent or OS sniffing)?

Original source

Related problems