Switch On The Code RSS Button - Click to Subscribe
Oct
31

Javascript Tutorial - The Scroll Wheel

So in Monday's post (about the Spin Control), I briefly mentioned using the mouse scroll wheel in javascript. Actually, what I really said was that you could use the scroll wheel with the spin control, and it would increment/decrement the spin control value as you moved the wheel. But I didn't go into any details at all as to how you can actually do this. Well, today, we are going to take a look at how to get scroll wheel information in javascript.

Below, there is a little box (which should say "Scroll In Me" unless you already played around). If your mouse cursor is over that box and you scroll the scroll wheel, it should print out two numbers in the box. The first number is the amount that the browser said your scroll wheel moved (which can be different depending on the bowser), and the second number is a 'normalized' version, which should be the same across all browsers.


Scroll In Me!

Amusing, eh? Well, it actually isn't that hard to do at all. In the end, it is just a different event to attach to, and then you have to grab data off of the result event object. The weirdness comes in because IE and Firefox have very different names for the scroll event. In Internet Explorer, the event is called onmousewheel, while in Firefox the event is called DOMMouseScroll. To make it even worse, Opera uses the Internet Explorer event name (without the 'on' part, so just mousewheel), but Opera needs to use the Firefox way of attaching event listeners.

Fortunately, it is pretty easy to wrap up. Remember the hookEvent and unhookEvent functions from way back in the Javascript Events tutorial? Well, they are about to get a small update:

function hookEvent(element, eventName, callback)
{
  if(typeof(element) == "string")
    element = document.getElementById(element);
  if(element == null)
    return;
  if(element.addEventListener)
  {
    if(eventName == 'mousewheel')
    {
      element.addEventListener('DOMMouseScroll',
        callback, false)
    }
    element.addEventListener(eventName, callback, false);
  }
  else if(element.attachEvent)
    element.attachEvent("on" + eventName, callback);
}

function unhookEvent(element, eventName, callback)
{
  if(typeof(element) == "string")
    element = document.getElementById(element);
  if(element == null)
    return;
  if(element.removeEventListener)
  {
    if(eventName == 'mousewheel')
    {
      element.removeEventListener('DOMMouseScroll',
        callback, false)
    }
    element.removeEventListener(eventName, callback, false);
  }
  else if(element.detachEvent)
    element.detachEvent("on" + eventName, callback);
}

Essentially, these two functions will let you always call the event mousewheel - in keeping with most of the other event names. Essentially, for Internet Explorer, we just add the 'on' part when attaching it (and we already had to do that for all the other events), and for Firefox/Opera/Safari we just attach both mousewheel and DOMMouseScroll. We can do this because none of the browsers mind if we attach to an event that doesn't exist - so Firefox doesn't care about mousewheel and Opera doesn't care about DOMMouseScroll.

But, sadly, that is only half the battle. There are two ways that the browsers return scroll wheel data. Thankfully, they are both on the event object - detail (for Firefox and Opera), and wheelData (for Internet Explorer, Safari, and Opera). Opera went and implemented both for some reason. So a function to get wheel data might look something like this:
function MouseWheel(e)
{
  e = e ? e : window.event;
  var wheelData = e.detail ? e.detail : e.wheelDelta;
  //do something
}

And that would be it, except for the fact that not only do the browsers use different property names, the scaling of the data is different. For instance, a value of 3 from Firefox or Opera in detail is equal to a value of 120 in wheelDelta for Internet Explorer or Safari. Oh, and don't forget that scrolling down is a positive number for detail, but a negative number for wheelDelta. All that can be easily remedied, though, with a quick adjustment:

function MouseWheel(e)
{
  e = e ? e : window.event;
  var wheelData = e.detail ? e.detail * -1 : e.wheelDelta / 40;
  //do something
}

What this function does now is always return things using the Firefox scaling, but makes scrolling down negative (which I think makes the most sense). It is easy enough to change if you would like the final result in a different form. Generally, I've found that a result of 3 from this function is equal to a single click of the scroll wheel, but it actually can depend on your operating system and the type of mouse you are using.

There is one other thing that you are probably wondering. How do you make the page stop from scrolling when you are capturing the scroll event? Well, that is actually quite easy. Again, we are going to draw on the Events tutorial, this time grabbing the cancelEvent function:

function cancelEvent(e)
{
  e = e ? e : window.event;
  if(e.stopPropagation)
    e.stopPropagation();
  if(e.preventDefault)
    e.preventDefault();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
  return false;
}

If you cancel the scroll wheel event, the page itself will not scroll. So, for instance, we can modify our above MouseWheel function to read:

function MouseWheel(e)
{
  e = e ? e : window.event;
  var wheelData = e.detail ? e.detail * -1 : e.wheelDelta / 40;
  //do something
  return cancelEvent(e);
}

With that function, any scroll event that made it into that function would have no affect on the actual scrolling of the page.

So lets put all that together, and recreate the example from the top of the tutorial. First, the really simple html:

<div style="width:530px;height:75px;
    border:1px solid black;"
id="scrollContent">

</div>

Now for the javascript (using the cancelEvent and hookEvent functions from above:

function printInfo(e)
{
  e = e ? e : window.event;
  var raw = e.detail ? e.detail : e.wheelDelta;
  var normal = e.detail ? e.detail * -1 : e.wheelDelta / 40;
  document.getElementById('scrollContent').innerHTML =
    "<br/>&nbsp;Raw Value: " + raw +
    "<br/>&nbsp;Normalized Value: " + normal;
  cancelEvent(e);
}

hookEvent('scrollContent', 'mousewheel', printInfo);

And that is that! Hopefully you are now enlightened when it comes to the mouse wheel and javascript, and as always, if you have any questions or comments, feel free to post them below.



Posted in Javascript, All Tutorials by The Tallest |

9 Responses

  1. ghane Says:

    thanks

  2. The functionality for (e) ? e : window.event; not clear Says:

    The functionality for (e) ? e : window.event; not clear

  3. The Tallest Says:

    What it means is that if e is undefined/null (i.e., ‘false’), we need to use the event object from window.event. You can learn why this check has to happen in the Working With Events tutorial.

  4. Aditya Says:

    Many thanks, this info was very helpful, I badly needed a discrete scrollbar - I mean scroll bar cannot be dragged to any part of the page, now I have my own custom scroll bar for this, instead of using overflow:scroll;

  5. Petulka7 Says:

    Perfect!
    This is exactly what I needed.
    I have Google map on my site and need to deactivate page scrolling when zooming the map with wheel scroll. This solved my problem.
    Thank you.

  6. Mark Says:

    Hi, great article.
    I’m trying to find out if it is possible to prevent zooming in IE7 (ctrl-mousewheel, ctrl+ / ctrl-). Has anyone got some ideas?
    thanks

  7. constantin Says:

    mark try to detect ctrl press then deactivate event key ctrl

  8. Aseem Says:

    Argh — amazing amazing article but one bug in here had me ripping my hair for 30 minutes trying to figure out why IE wouldn’t register any value!!

    You wrote:

    function MouseWheel(e)
    {
    e = e ? e : window.event;
    var wheelData = e.detail ? e.detail : e.wheelData;
    //do something
    }

    (This is the third box of code in the article.)

    But that last variable should be e.wheelDelta, not e.wheelData.

    =) Thanks though!

  9. The Tallest Says:

    Good catch. I’ve fixed the code block which had that typo. Thanks!

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

Powered by WP Hashcash