64

I want to run a function when someone scrolls down on an element. Something like this:

 $('div').scrollDown(function(){ alert('down') });
 $('div').scrollUp(function(){ alert('up') });

But those functions don't exist. Is there a solution to this problem? Awwwards seem to be able to do it (logo in navbar changes depending on scroll direction). Unfortunately, the source code is compressed, so no luck there.

Aryan Beezadhur
  • 4,503
  • 4
  • 21
  • 42
Rik de Vos
  • 3,467
  • 5
  • 28
  • 34

8 Answers8

108

I managed to figure it out in the end, so if anyone is looking for the answer:

 //Firefox
 $('#elem').bind('DOMMouseScroll', function(e){
     if(e.originalEvent.detail > 0) {
         //scroll down
         console.log('Down');
     }else {
         //scroll up
         console.log('Up');
     }

     //prevent page fom scrolling
     return false;
 });

 //IE, Opera, Safari
 $('#elem').bind('mousewheel', function(e){
     if(e.originalEvent.wheelDelta < 0) {
         //scroll down
         console.log('Down');
     }else {
         //scroll up
         console.log('Up');
     }

     //prevent page fom scrolling
     return false;
 });
yoda
  • 10,834
  • 19
  • 64
  • 92
Rik de Vos
  • 3,467
  • 5
  • 28
  • 34
  • 5
    This should be added to JQuery – Mark Vital Oct 20 '12 at 23:11
  • 1
    should be e.originalEvent.wheelDelta – szanata Nov 26 '12 at 16:24
  • 3
    Keep in mind that as of jQuery 1.7, on() is the preferred way to attach event handlers. – theblang Jan 10 '13 at 05:42
  • 3
    Actually, nor `e.wheelDelta`, nor `e.delta`, but `e.originalEvent.wheelDelta` – sadfuzzy Jan 20 '13 at 14:00
  • Thanks, it saves time @Rik de Vos – Ana DEV Apr 05 '17 at 13:01
  • If using [jquery mousewheel plugin](https://github.com/jquery/jquery-mousewheel), then I would recommend the use of the 2nd argument of event handler function - `delta`: `$('#el').on('mousewheel', function(e, delta) { if(delta > 0) { /*up*/ } else { /*down*/ }})`. See [this answer](http://stackoverflow.com/a/43468489/1245149). – Mojo Apr 18 '17 at 09:44
51

Following example will listen to MOUSE scroll only, no touch nor trackpad scrolls.

It uses jQuery.on() (As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document).

$('#elem').on( 'DOMMouseScroll mousewheel', function ( event ) {
  if( event.originalEvent.detail > 0 || event.originalEvent.wheelDelta < 0 ) { //alternative options for wheelData: wheelDeltaX & wheelDeltaY
    //scroll down
    console.log('Down');
  } else {
    //scroll up
    console.log('Up');
  }
  //prevent page fom scrolling
  return false;
});

Works on all browsers.

fiddle: http://jsfiddle.net/honk1/gWnNv/7/

honk31
  • 3,895
  • 3
  • 31
  • 30
  • 1
    just tested on IE9, Chrome 42 and Firefox 37. nice. i had a problem with this in mozilla b4. this fixes that. – dewd Apr 21 '15 at 10:47
  • I tried this solution, but it didn't work on FF (49.0.1), but adding 'MozMousePixelScroll' to the events fixed it. – zee Sep 29 '16 at 14:23
48

This one deserves an update - nowadays we have the wheel event :

$(function() {

$(window).on('wheel', function(e) {

 var delta = e.originalEvent.deltaY;

 if (delta > 0) $('body').text('down');
 else $('body').text('up');

 return false; // this line is only added so the whole page won't scroll in the demo
});
});
body {
  font-size: 22px;
  text-align: center;
  color: white;
  background: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Support has been pretty good on modern browsers for quite a while already :

  • Chrome 31+
  • Firefox 17+
  • IE9+
  • Opera 18+
  • Safari 7+

https://developer.mozilla.org/en-US/docs/Web/Events/wheel

If deeper browser support is required, probably best to use mousewheel.js instead of messing about :

https://plugins.jquery.com/mousewheel/

$(function() {

$(window).mousewheel(function(turn, delta) {

 if (delta > 0) $('body').text('up');
 else $('body').text('down');

 return false; // this line is only added so the whole page won't scroll in the demo
});
});
body {
  font-size: 22px;
  text-align: center;
  color: white;
  background: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js"></script>
Shikkediel
  • 5,195
  • 16
  • 45
  • 77
  • 1
    Only problem: On websites longer than viewport-height, the event only fires when the scroll wheel stopped scolling, not in the moment of the mouse-wheel movement. – Noah Krasser Aug 24 '16 at 22:21
  • Maybe you could elaborate on what you mean. One can't do panning with a mouse turn obviously and the only issue I'm aware of that comes close to your description is the fact that IE has a slight delay between the mousewheel being turned and the scroll event firing. – Shikkediel Nov 30 '16 at 17:13
24

Existing Solution

There could be 3 solution from this posting and other stackoverflow article.

Solution 1

    var lastScrollTop = 0;
    $(window).on('scroll', function() {
        st = $(this).scrollTop();
        if(st < lastScrollTop) {
            console.log('up 1');
        }
        else {
            console.log('down 1');
        }
        lastScrollTop = st;
    });

Solution 2

    $('body').on('DOMMouseScroll', function(e){
        if(e.originalEvent.detail < 0) {
            console.log('up 2');
        }
        else {
            console.log('down 2');
        }
    });

Solution 3

    $('body').on('mousewheel', function(e){
        if(e.originalEvent.wheelDelta > 0) {
            console.log('up 3');
        }
        else {
            console.log('down 3');
        }
    });

Multi Browser Test

I couldn't tested it on Safari

chrome 42 (Win 7)

  • Solution 1
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : Not working
  • Solution 3
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll

Firefox 37 (Win 7)

  • Solution 1
    • Up : 20 events per 1 scroll
    • Down : 20 events per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : 1 event per 1 scroll
  • Solution 3
    • Up : Not working
    • Down : Not working

IE 11 (Win 8)

  • Solution 1
    • Up : 10 events per 1 scroll (side effect : down scroll occured at last)
    • Down : 10 events per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : Not working
  • Solution 3
    • Up : Not working
    • Down : 1 event per 1 scroll

IE 10 (Win 7)

  • Solution 1
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : Not working
  • Solution 3
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll

IE 9 (Win 7)

  • Solution 1
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : Not working
  • Solution 3
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll

IE 8 (Win 7)

  • Solution 1
    • Up : 2 events per 1 scroll (side effect : down scroll occured at last)
    • Down : 2~4 events per 1 scroll
  • Soltion 2
    • Up : Not working
    • Down : Not working
  • Solution 3
    • Up : 1 event per 1 scroll
    • Down : 1 event per 1 scroll

Combined Solution

I checked that side effect from IE 11 and IE 8 is come from if else statement. So, I replaced it with if else if statement as following.

From the multi browser test, I decided to use Solution 3 for common browsers and Solution 1 for firefox and IE 11.

I referred this answer to detect IE 11.

    // Detect IE version
    var iev=0;
    var ieold = (/MSIE (\d+\.\d+);/.test(navigator.userAgent));
    var trident = !!navigator.userAgent.match(/Trident\/7.0/);
    var rv=navigator.userAgent.indexOf("rv:11.0");

    if (ieold) iev=new Number(RegExp.$1);
    if (navigator.appVersion.indexOf("MSIE 10") != -1) iev=10;
    if (trident&&rv!=-1) iev=11;

    // Firefox or IE 11
    if(typeof InstallTrigger !== 'undefined' || iev == 11) {
        var lastScrollTop = 0;
        $(window).on('scroll', function() {
            st = $(this).scrollTop();
            if(st < lastScrollTop) {
                console.log('Up');
            }
            else if(st > lastScrollTop) {
                console.log('Down');
            }
            lastScrollTop = st;
        });
    }
    // Other browsers
    else {
        $('body').on('mousewheel', function(e){
            if(e.originalEvent.wheelDelta > 0) {
                console.log('Up');
            }
            else if(e.originalEvent.wheelDelta < 0) {
                console.log('Down');
            }
        });
    }
Community
  • 1
  • 1
Chemical Programmer
  • 4,352
  • 4
  • 37
  • 51
  • 1
    Oops! I found that Mobile Chrome and Android default browser are only covered by Solution 1. So it would be better to use both Solution 1 and 3 together to cover various browsers. – Chemical Programmer Apr 18 '15 at 18:25
12
$(function(){
    var _top = $(window).scrollTop();
    var _direction;
    $(window).scroll(function(){
        var _cur_top = $(window).scrollTop();
        if(_top < _cur_top)
        {
            _direction = 'down';
        }
        else
        {
            _direction = 'up';
        }
        _top = _cur_top;
        console.log(_direction);
    });
});

Demo: http://jsfiddle.net/AlienWebguy/Bka6F/

AlienWebguy
  • 76,997
  • 17
  • 122
  • 145
1

Here is a sample showing an easy way to do it. The script is:

$(function() {
  var _t = $("#container").scrollTop();
  $("#container").scroll(function() {
    var _n = $("#container").scrollTop();
    if (_n > _t) {
      $("#target").text("Down");
    } else {
      $("#target").text("Up");
    }
    _t = _n;
  });
});

The #container is your div id. The #target is just to see it working. Change to what you want when up or when down.

EDIT

The OP didn't say before, but since he's using a div with overflow: hidden, scrolling doesn't occur, then the script to detect the scroll is the least of it. Well, how to detect something that does not happen?!

So, the OP himself posted the link with what he wants, so why not use that library? http://cdn.jquerytools.org/1.2.5/full/jquery.tools.min.js.

The call is just:

$(function() {
    $(".scrollable").scrollable({ vertical: true, mousewheel: true });
});
Erick Petrucelli
  • 14,386
  • 8
  • 64
  • 84
  • Sure, that works, but you'd have to set the overflow to "auto" and I have it set to "hidden". If I could have set the overflow to "auto" or "scroll", I wouldn't have asked this question ;-) – Rik de Vos Aug 23 '11 at 00:31
0
var mousewheelevt = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x
$(document).bind(mousewheelevt, 
function(e)
    {
        var evt = window.event || e //equalize event object     
        evt = evt.originalEvent ? evt.originalEvent : evt; //convert to originalEvent if possible               
        var delta = evt.detail ? evt.detail*(-40) : evt.wheelDelta //check for detail first, because it is used by Opera and FF
        if(delta > 0) 
            {
            scrollup();
            }
        else
            {
            scrolldown();
            }   
    }
);
Jithin U. Ahmed
  • 1,495
  • 1
  • 19
  • 29
0

You can use this simple plugin to add scrollUp and scrollDown to your jQuery

https://github.com/phpust/JQueryScrollDetector

var lastScrollTop = 0;
var action = "stopped";
var timeout = 100;
// Scroll end detector:
$.fn.scrollEnd = function(callback, timeout) {    
      $(this).scroll(function(){
        // get current scroll top 
        var st = $(this).scrollTop();
        var $this = $(this);
        // fix for page loads
        if (lastScrollTop !=0 )
        {
            // if it's scroll up
            if (st < lastScrollTop){
                action = "scrollUp";
            } 
            // else if it's scroll down
            else if (st > lastScrollTop){
                action = "scrollDown";
            }
        }
        // set the current scroll as last scroll top
        lastScrollTop = st;
        // check if scrollTimeout is set then clear it
        if ($this.data('scrollTimeout')) {
          clearTimeout($this.data('scrollTimeout'));
        }
        // wait until timeout done to overwrite scrolls output
        $this.data('scrollTimeout', setTimeout(callback,timeout));
    });
};

$(window).scrollEnd(function(){
    if(action!="stopped"){
        //call the event listener attached to obj.
        $(document).trigger(action); 
    }
}, timeout);
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
S.Kashizadeh
  • 563
  • 4
  • 14