186

So I am trying to use the JavaScript on scroll to call a function. But I wanted to know if I could detect the direction of the the scroll without using jQuery. If not then are there any workarounds?

I was thinking of just putting a 'to top' button but would like to avoid that if I could.

I have now just tried using this code but it didn't work:

if document.body.scrollTop <= 0 {
    alert ("scrolling down")
} else {
    alert ("scrolling up")
}
isherwood
  • 52,576
  • 15
  • 105
  • 143
dwinnbrown
  • 3,421
  • 8
  • 32
  • 59

13 Answers13

270

It can be detected by storing the previous scrollTop value and comparing the current scrollTop value with it.

JavaScript :

var lastScrollTop = 0;

// element should be replaced with the actual target element on which you have applied scroll, use window in case of no target element.
element.addEventListener("scroll", function(){ // or window.addEventListener("scroll"....
   var st = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426"
   if (st > lastScrollTop){
      // downscroll code
   } else {
      // upscroll code
   }
   lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
}, false);
Littm
  • 4,903
  • 4
  • 29
  • 37
Prateek
  • 3,772
  • 1
  • 16
  • 21
  • 38
    It would be safer to initialize `lastScrollTop` to pageYOffset || scrollTop rather than assuming 0 – Ed Ballot Jul 04 '15 at 18:17
  • Totally agree !! Thanks @EdBallot. We should initialize the same on window.onload event. – Prateek Jul 04 '15 at 18:34
  • @Prateek Thanks for your answer but it isn't working for me... I am trying to 'change scene' in my webapp which is built using Tumult Hype. – dwinnbrown Jul 04 '15 at 18:49
  • I have added few comments in my answer, please check it. I guess you are using "element.addEventListener". – Prateek Jul 04 '15 at 18:59
  • @Prateek still nothing I'm afraid. Could it be to do with the fact that, I am running it on page load? Here is a screenshot: http://i.imgur.com/Q0H0T4s.png – dwinnbrown Jul 04 '15 at 19:05
  • It would be great, if you can create fiddle for the same on jsfiddle.net – Prateek Jul 04 '15 at 19:10
  • I would but as I am using a software specific js attribute it wouldn't run properly...essentially on scroll down I want to redirect to a new url – dwinnbrown Jul 04 '15 at 19:17
  • In IE 11 this does not work, please see the answer from @Emmanual https://stackoverflow.com/a/45139285/996010 – Hrvoje Golcic Jun 28 '18 at 13:54
  • follow lesson 13 in Wes Bos JavaScript30 you should wrapper it with debounce function so it will not run so much each second. https://codesandbox.io/s/loving-hamilton-b85f7 – Steve Phuc Feb 13 '20 at 02:17
  • what is negative scrolling? per this [answer](https://stackoverflow.com/a/62530146/5107146), browsers do not allow negative scrolling, so what is that for? – mockingjay Jun 09 '21 at 08:59
76

Simple way to catch all scroll events (touch and wheel)

window.onscroll = function(e) {
  // print "false" if direction is down and "true" if up
  console.log(this.oldScroll > this.scrollY);
  this.oldScroll = this.scrollY;
}
IT VLOG
  • 825
  • 6
  • 5
52

Use this to find the scroll direction. This is only to find the direction of the Vertical Scroll. Supports all cross browsers.

var scrollableElement = document.body; //document.getElementById('scrollableElement');

scrollableElement.addEventListener('wheel', checkScrollDirection);

function checkScrollDirection(event) {
  if (checkScrollDirectionIsUp(event)) {
    console.log('UP');
  } else {
    console.log('Down');
  }
}

function checkScrollDirectionIsUp(event) {
  if (event.wheelDelta) {
    return event.wheelDelta > 0;
  }
  return event.deltaY < 0;
}

Example

Community
  • 1
  • 1
Vasi
  • 1,059
  • 9
  • 16
  • 2
    This is good, but only seems to work for using the scroll wheel – Jonathan.Brink Oct 02 '18 at 20:03
  • 10
    From MDN webdocs: Note: Don't confuse the wheel event with the scroll event. The default action of a wheel event is implementation-specific, and doesn't necessarily dispatch a scroll event. Even when it does, the delta* values in the wheel event don't necessarily reflect the content's scrolling direction. Therefore, do not rely on the wheel event's delta* properties to get the scrolling direction. Instead, detect value changes of scrollLeft and scrollTop of the target in the scroll event. https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event – prettyInPink May 12 '20 at 15:31
  • 1
    This one is good (compared to `scroll` event) because it fires also after reaching the end or start of scroll location - sometimes you may want to fire some specific action "to scroll over" the end of the element. – lenooh Sep 09 '20 at 15:07
  • 1
    Thanks! Very useful in "fixed" positioned sections! – Oleksa O. Dec 03 '20 at 09:45
14

You can try doing this.

function scrollDetect(){
  var lastScroll = 0;

  window.onscroll = function() {
      let currentScroll = document.documentElement.scrollTop || document.body.scrollTop; // Get Current Scroll Value

      if (currentScroll > 0 && lastScroll <= currentScroll){
        lastScroll = currentScroll;
        document.getElementById("scrollLoc").innerHTML = "Scrolling DOWN";
      }else{
        lastScroll = currentScroll;
        document.getElementById("scrollLoc").innerHTML = "Scrolling UP";
      }
  };
}


scrollDetect();
html,body{
  height:100%;
  width:100%;
  margin:0;
  padding:0;
}

.cont{
  height:100%;
  width:100%;
}

.item{
  margin:0;
  padding:0;
  height:100%;
  width:100%;
  background: #ffad33;
}

.red{
  background: red;
}

p{
  position:fixed;
  font-size:25px;
  top:5%;
  left:5%;
}
<div class="cont">
  <div class="item"></div>
  <div class="item red"></div>
  <p id="scrollLoc">0</p>
</div>
davecar21
  • 2,486
  • 21
  • 32
10
  1. Initialize an oldValue
  2. Get the newValue by listening to the event
  3. Subtract the two
  4. Conclude from the result
  5. Update oldValue with the newValue

// Initialization

let oldValue = 0;

//Listening on the event

window.addEventListener('scroll', function(e){

    // Get the new Value
    newValue = window.pageYOffset;

    //Subtract the two and conclude
    if(oldValue - newValue < 0){
        console.log("Up");
    } else if(oldValue - newValue > 0){
        console.log("Down");
    }

    // Update the old value
    oldValue = newValue;
});
thewebjackal
  • 590
  • 4
  • 14
9

This is an addition to what prateek has answered.There seems to be a glitch in the code in IE so i decided to modify it a bit nothing fancy(just another condition)

$('document').ready(function() {
var lastScrollTop = 0;
$(window).scroll(function(event){
   var st = $(this).scrollTop();
   if (st > lastScrollTop){
       console.log("down")
   }
   else if(st == lastScrollTop)
   {
     //do nothing 
     //In IE this is an important condition because there seems to be some instances where the last scrollTop is equal to the new one
   }
   else {
      console.log("up")
   }
   lastScrollTop = st;
});});
Emmanual
  • 124
  • 1
  • 7
5

This simple code would work: Check the console for results.

let scroll_position = 0;
let scroll_direction;

window.addEventListener('scroll', function(e){
    scroll_direction = (document.body.getBoundingClientRect()).top > scroll_position ? 'up' : 'down';
    scroll_position = (document.body.getBoundingClientRect()).top;
    console.log(scroll_direction);
});
Usman Ahmed
  • 2,243
  • 1
  • 17
  • 17
  • Calling `getBoundingClientRect` forces a layout of the element (in this case, the entire body) every time it is called. It is best therefore to call it once per scroll event and cache the result... – Heretic Monkey Oct 05 '21 at 15:34
5

While the accepted answer works, it is worth noting that this will fire at a high rate. This can cause performance issues for computationally expensive operations.

The recommendation from MDN is to throttle the events. Below is a modification of their sample, enhanced to detect scroll direction.

Modified from: https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event

// ## function declaration
function scrollEventThrottle(fn) {
  let last_known_scroll_position = 0;
  let ticking = false;
  window.addEventListener("scroll", function () {
    let previous_known_scroll_position = last_known_scroll_position;
    last_known_scroll_position = window.scrollY;
    if (!ticking) {
      window.requestAnimationFrame(function () {
        fn(last_known_scroll_position, previous_known_scroll_position);
        ticking = false;
      });
      ticking = true;
    }
  });
}

// ## function invocation
scrollEventThrottle((scrollPos, previousScrollPos) => {
    if (previousScrollPos > scrollPos) {
      console.log("going up");
    } else {
      console.log("going down");
    }
});

theUtherSide
  • 3,108
  • 3
  • 34
  • 35
3

You can get the scrollbar position using document.documentElement.scrollTop. And then it is simply matter of comparing it to the previous position.

Igal S.
  • 11,488
  • 4
  • 31
  • 44
  • Ok and could I still use this on a website which won't traditionally allow scrolling (i.e. it fits the browser 100% width and height. Thanks – dwinnbrown Jul 04 '15 at 17:35
2

I personally use this code to detect scroll direction in javascript... Just you have to define a variable to store lastscrollvalue and then use this if&else

let lastscrollvalue;

function headeronscroll() {

    // document on which scroll event will occur
    var a = document.querySelector('.refcontainer'); 

    if (lastscrollvalue == undefined) {

        lastscrollvalue = a.scrollTop;

        // sets lastscrollvalue
    } else if (a.scrollTop > lastscrollvalue) {

        // downscroll rules will be here
        lastscrollvalue = a.scrollTop;

    } else if (a.scrollTop < lastscrollvalue) {

        // upscroll rules will be here
        lastscrollvalue = a.scrollTop;

    }
}
notapatch
  • 5,708
  • 5
  • 36
  • 40
WagonWolf
  • 21
  • 1
1

If anyone looking to achieve it with React hooks

  const [scrollStatus, setScrollStatus] = useState({
    scrollDirection: null,
    scrollPos: 0,
  });

  useEffect(() => {
    window.addEventListener("scroll", handleScrollDocument);

    return () => window.removeEventListener("scroll", handleScrollDocument);
  }, []);

  function handleScrollDocument() {
    setScrollStatus((prev) => { // to get 'previous' value of state
      return {
        scrollDirection:
          document.body.getBoundingClientRect().top > prev.scrollPos
            ? "up"
            : "down",
        scrollPos: document.body.getBoundingClientRect().top,
      };
    });
  }

  console.log(scrollStatus.scrollDirection)
Kaung Khant Zaw
  • 1,163
  • 1
  • 9
  • 23
1

Modifying Prateek's answer, if there is no change in lastScrollTop, then it would be a horizontal scroll (with overflow in the x direction, can be used by using horizontal scrollbars with a mouse or using scrollwheel + shift.

const containerElm = document.getElementById("container");

let lastScrollTop = containerElm.scrollTop;

containerElm.addEventListener("scroll", (evt) => {
  const st = containerElm.scrollTop;

  if (st > lastScrollTop) {
    console.log("down scroll");
  } else if (st < lastScrollTop) {
    console.log("up scroll");
  } else {
    console.log("horizontal scroll");
  }

  lastScrollTop = Math.max(st, 0); // For mobile or negative scrolling
});
Justin Golden
  • 11
  • 1
  • 2
0

This seems to be working fine.

document.addEventListener('DOMContentLoaded', () => {

    var scrollDirectionDown;
    scrollDirectionDown = true;

    window.addEventListener('scroll', () => {

        if (this.oldScroll > this.scrollY) {
            scrollDirectionDown = false;
        } else {
            scrollDirectionDown = true;
        }
        this.oldScroll = this.scrollY;


        // test
        if (scrollDirectionDown) {
            console.log('scrolling down');
        } else {
            console.log('scrolling up');
        }



    });
});
Maciek Rek
  • 1,249
  • 2
  • 12
  • 18