dzimi dzimi - 6 months ago 32
Javascript Question

Detecting scroll direction - boolean variable wrong on first scroll change direction

I want to detect whether I am scrolling up or down. I also have a global variable which should be

true
if scrolling down and
false
if up.

The problem is, if I scroll down once, and keep scrolling down, the console logs
true
and
down
, but if I then scroll up just once, it logs 'up' correctly but also
true
instead of
false
. Same goes when scrolling the other way around.

So basically the
down
variable is wrong after first scroll change which changes direction.

<div id="id0" data-id="0" class="nbhd red scrolledto"></div>
<div id="id1" data-id="1" class="nbhd yellow"></div>
<div id="id2" data-id="2" class="nbhd green"></div>
<div id="id3" data-id="3" class="nbhd blue"></div>

var down = true;

function setHeights() {
$('.nbhd').css('height', window.innerHeight);
}

function scrollDirection() {
var lastScrollTop = $(window).scrollTop();

$(window).scroll(function(event) {

var st = $(this).scrollTop();

if (st > lastScrollTop){
// down
down = true;
console.log('down');
} else if (st < lastScrollTop) {
// up
down = false;
console.log('up');
}

lastScrollTop = st;

});

console.log(down);
}

setHeights();

$( window ).on( "scroll", function() {
scrollDirection();
});


fiddle: https://jsfiddle.net/gh3cgrqL/

Answer

Part of the problem is that you're hooking the scroll event every time anything scrolls, because you have this:

$( window ).on( "scroll", function() {
    scrollDirection();
});

...which calls scrollDirection every time there's any scrolling, and within scrollDirection you have this:

$(window).scroll(function(event) {
    // ...
});

...which adds another handler. Each time. After a relatively small amount of scrolling the page will become sluggish and ultimately non-responsive as all those event hanlders stack up.

Separately, since scrollDirection is called repeatedly, your lastScroll is updated before the inner handlers have a chance to work with it.

Just hook up a single handler, and remember the last scroll position:

var lastScroll = 0;
var down = false;
$(window).on("scroll", function() {
    var scroll = $(this).scrollTop();
    down = scroll > lastScroll;
    lastScroll = scroll;
});

Live Example:

(function() {
    var lastScroll = 0;
    var down = false;
    $(window).on("scroll", function() {
        var scroll = $(this).scrollTop();
        down = scroll > lastScroll;
        lastScroll = scroll;
        $("#indicator").text(down ? "down" : "up");
    });
})();
<div id="indicator" style="position: fixed; top: 0; left: 0; right: 0"><em>(No scroll yet)</em></div>
<div style="margin-top: 1.1em">
<div>
  scroll over this
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
  <br>.
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>