216

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
  • 58,414
  • 16
  • 114
  • 157
dwinnbrown
  • 3,789
  • 9
  • 35
  • 60

17 Answers17

314

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 if (st < lastScrollTop) {
      // upscroll code
   } // else was horizontal scroll
   lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
}, false);
Jake
  • 948
  • 8
  • 19
Prateek
  • 4,220
  • 1
  • 17
  • 22
  • 52
    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
  • 1
    @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
  • 1
    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
  • 1
    what if the current position is on top of the list, the previous scrollTop and the current one are equal to 0 – Hoangdz Jul 18 '22 at 00:49
  • @Hoangdz, sure `else` in that case – axdrv Dec 09 '22 at 22:22
  • 1
    @mockingjay Touch devices allow slight scrolling beyond bounds. This gives the user visual cues that they have reached the limit of scrolling. When you query the DOM attributes during such animation, you may find values that are out of bounds. The test for `<= 0` handles that situation. A similar situation may arise at the other end. – Jake Jan 26 '23 at 03:32
  • I've edited the answer so as not to execute code in the case of a horizontal scroll (when `st` is the same as `lastScrollTop`), which could in theory occur if the element happens to overflow horizontally and is scrolled in that axis (e.g. via swipe even if there are no visible scrollbars). – Jake Jan 29 '23 at 02:04
90

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
  • 961
  • 6
  • 5
60

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,147
  • 9
  • 16
  • 2
    This is good, but only seems to work for using the scroll wheel – Jonathan.Brink Oct 02 '18 at 20:03
  • 13
    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
  • 2
    Thanks! Very useful in "fixed" positioned sections! – Oleksa O. Dec 03 '20 at 09:45
18

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,606
  • 21
  • 33
12
  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
  • 785
  • 8
  • 17
10

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,338
  • 4
  • 36
  • 35
Vincent Manera
  • 113
  • 2
  • 6
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
  • 8
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,392
  • 1
  • 20
  • 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
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.
  • 13,146
  • 5
  • 30
  • 48
  • 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
3

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,508
  • 3
  • 19
  • 31
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
  • 6,569
  • 6
  • 41
  • 45
WagonWolf
  • 21
  • 2
2

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
  • 41
  • 1
  • 6
1

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,525
  • 2
  • 14
  • 18
1

Sometimes there are inconsistencies in scrolling behavior which does not properly update the scrollTop attribute of an element. It would be safer to put some threshold value before deciding the scroll direction.

let lastScroll = 0
let threshold = 10 // must scroll by 10 units to know the direction of scrolling


element.addEventListener("scroll", () => {
    let newScroll = element.scrollTop
    
    if (newScroll - lastScroll > threshold) {
        // "up" code here
    } else if (newScroll - lastScroll < -threshold) {
        // "down" code here
    }
    
    lastScroll = newScroll

})
Amia
  • 96
  • 6
1
let arrayScroll = [];
        window.addEventListener('scroll', ()=>{
            arrayScroll.splice(1); //deleting unnecessary data so that array does not get too big
            arrayScroll.unshift(Math.round(window.scrollY));
            if(arrayScroll[0] > arrayScroll[1]){
                console.log('scrolling down');
            } else{
                console.log('scrolling up');
            }
        })

I have self-made the above solution. I am not sure if this solution may cause any considerable performance issue comparing other solutions as I have just started learning JS and not yet have completed my begginer course. Any suggestion or advice from experienced coder is highly appriciated. ThankYou!

0

Down = -1, up = 1

window.addEventListener("wheel", e => {
    const scrollDirection = e.deltaY < 0 ? 1 : -1;
    console.log(scrollDirection)
}
Anm
  • 447
  • 4
  • 15
0

There is an easiest way to determine if goes to top, goes down, left or right, just check if wheelDelta > 0 or wheelDelta < 0

enter image description here

Marvin
  • 647
  • 7
  • 15