1

I'm trying to get the correct scroll direction via jQuery's "scroll" event.

For this, I'm using the solution here: https://stackoverflow.com/a/4326907/8407840

However, if I change the direction of my scroll, the offset returned by scrollTop is incorrect on the first time. This results in the following behavior:

  1. Wheel down -> down
  2. Wheel down -> down
  3. Wheel up -> down
  4. Wheel up -> up
  5. Wheel down -> up
  6. Wheel down -> down
  7. ... and so on, I think you get it.

var ACTIVE_SECTION = null;
var ANIMATION_DURATION = 700;

$(document).ready(function() {
  ACTIVE_SECTION = $("section:first-of-type").get(0);
  var prevPosition = $(window).scrollTop();
  
  $(window).on("scroll", function() {
    doScrollingStuff(prevPosition);
  });

});

function doScrollingStuff(prevPosition) {
  var ctPosition = $(window).scrollTop();
  var nextSection = ACTIVE_SECTION;
  
  // Remove and re-append event, to prevent it from firing too often.
  $(window).off("scroll");
  setTimeout(function() {
    $(window).on("scroll", function() {
      doScrollingStuff(prevPosition);
    });
  }, ANIMATION_DURATION + 100);

  // Determine scroll direction and target the next section
  if(ctPosition < prevPosition) {
    console.log("up");
    nextSection = $(ACTIVE_SECTION).prev("section").get(0);
  } else if(ctPosition > prevPosition) {
    console.log("down");
    nextSection = $(ACTIVE_SECTION).next("section").get(0);
  }
  
  // If a next section exists: Scroll to it!
  if(typeof nextSection != 'undefined') {
    var offset = $(nextSection).offset();
    $("body, html").animate({
      scrollTop: offset.top
    }, ANIMATION_DURATION);
    ACTIVE_SECTION = nextSection;
  } else {
    nextSection = ACTIVE_SECTION;
  }

  console.log(ACTIVE_SECTION);
  prevPosition = ctPosition;
}
section {
  width:100%;
  height:100vh;
  padding:60px;
  box-sizing:border-box;
}

section:nth-child(1) { background:#13F399; }
section:nth-child(2) { background:#14FD43; }
section:nth-child(3) { background:#4EE61E; }
section:nth-child(4) { background:#BEFD14; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="sect1">Section 1</section>
<section id="sect2">Section 2</section>
<section id="sect3">Section 3</section>
<section id="sect4">Section 4</section>

Here's a pen, where you can see my implementation: https://codepen.io/EigenDerArtige/pen/aVEyxd

I am trying to accomplish an autoscroll to the next or previous section, whenever the user scrolls or swipes up/down... Therefore I only fire the "scroll"-event once every second, to prevent multiple scrolljacks all happening at once... However the above behavior seems to result in the user being scrolled to the wrong section.

I've been trying for a couple of hours now to get it working, but to no avail. Help is greatly appreciated!

Eigen
  • 43
  • 8

1 Answers1

1

The problem lies in the assignment prevPosition = ctPosition.

Each time the scroll handler runs, var ctPosition = $(window).scrollTop(); is good for determining scroll direction, however it's not the value that should be rememberad as prevPosition.

prevPosition needs to be $(window).scrollTop() as measured after the animation has completed.

Try this :

$(document).ready(function() {
    var ANIMATION_DURATION = 700;
    var ACTIVE_SECTION = $("section:first-of-type").eq(0);
    var prevPosition = $(window).scrollTop();
    $(window).on("scroll", doScrollingStuff);

    function doScrollingStuff(e) {
        $(window).off("scroll");

        var ctPosition = $(window).scrollTop();
        var nextSection = (ctPosition < prevPosition) ? ACTIVE_SECTION.prev("section") : (ctPosition > prevPosition) ? ACTIVE_SECTION.next("section") : ACTIVE_SECTION; // Determine scroll direction and target the next section

        // If next section exists and is not current section: Scroll to it!
        if(nextSection.length > 0 && nextSection !== ACTIVE_SECTION) {
            $("body, html").animate({
                'scrollTop': nextSection.offset().top
            }, ANIMATION_DURATION).promise().then(function() {
                // when animation is complete
                prevPosition = $(window).scrollTop(); // remember remeasured .scrollTop()
                ACTIVE_SECTION = nextSection; // remember active section
                $(window).on("scroll", doScrollingStuff); // no need for additional delay after animation
            });
        } else {
            setTimeout(function() {
                $(window).on("scroll", doScrollingStuff);
            }, 100); // Debounce
        }
    }
});
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • Thank you very much, it works like a charm with the scroll wheel! However, when swiping, the scrolling stutters. Or, when the scroll is activated, multiple sections are scrolled through in one go. How can this be fixed? I've tried increasing the timeout-delays, but this didn't have any effect :/ – Eigen Nov 19 '17 at 12:02
  • Sorry, I don't really know much about swiping and don't have the means to test it. – Roamer-1888 Nov 19 '17 at 12:20
  • 1
    Np. It works so far, you've helped me a lot. Will try to get it to work with swipes :) – Eigen Nov 19 '17 at 12:57
  • Cool! Good luck with it. – Roamer-1888 Nov 19 '17 at 15:34