1

So I have two sections of content near the top of my page and I’d like for users who have scrolled down to near the top of the second section to get “scroll snapped” to the top of the second one once they have stopped scrolling.

I think it should be possible using jQuery but I haven’t been able to figure it out. Here are my examples:

Basically I can’t figure out how to make it try scrolling to the spot only once, after scrolling has stopped. It’s kind of just freaking out.

I love how the recently introduced scroll snap points CSS feature handles scroll snapping and I’d almost prefer to use it – for the browsers that support it, at least – but it seems like it only works for items that take up 100% of the viewport height or width, and it seems like it’s for scrolling within an element, not the page itself.

The top section has a fixed height, so this really can be handled with pixel numbers.


And for reference, here’s the heart of the code from my attempt:

$(function() {
  $(document).on('scroll', function() {
    var top = $(document).scrollTop();
    if (top > 255 && top < 455) {
      $('html, body').animate({scrollTop: '356'}, 500);
      $('body').addClass('hotzone');
    } else {
      $('body').removeClass('hotzone');
    }
  });
});
Joel Farris
  • 510
  • 1
  • 6
  • 23

2 Answers2

4

KQI's answer contains most of the steps required to create a well functioning section-scroll for use in your application/webpage.

However, if you'd just want to experiment yourself, developing your script further, the first thing you'll have to do is add a timeout handler. Otherwise your logic, and therefor scrollAnimation, will trigger every single pixel scrolled and create a buggy bouncing effect.

I have provided a working example based on your script here: http://codepen.io/anon/pen/QjepRZ?editors=001

$(function() {
  var timeout;
  $(document).on('scroll', function() {
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      var top = $(document).scrollTop();
      if (top > 255 && top < 455) {
        $('body').animate({
          scrollTop: '356'
        }, 500);
        $('body').addClass('hotzone');
      } else {
        $('body').removeClass('hotzone');
      }
    }, 50);

  });
});

Good luck!

woobione
  • 358
  • 2
  • 9
  • This is great and nearly perfect! Thanks for the suggestion. However, I’m still seeing a “buggy bouncing effect” when I attempt to scroll (a) either while it’s moving or (b) some time after it has snapped (if I’m scrolling slowly). Is there any way to prevent the snap from trying to work while a user is scrolling? – Joel Farris Nov 29 '15 at 22:36
  • Nice to be able to help :) As for the problems of scrolling slowly I think it might be hard to catch. Since your animation itself will actually trigger the scroll event I can't see a way to detect whether the user did the scrolling and stop the animation. I'll try some and get back to you. Don't forget to accept the answer if it helped you :) – woobione Dec 01 '15 at 14:48
  • 1
    I have an updated version here (http://codepen.io/anon/pen/adooGm?editors=001) with some extra checks for if the user initiated the scroll. The buginess persists, however, if you scroll very few pixels using the scrollbar which initiate another animation. That problem I can't find a solution to atm. Good luck! :) – woobione Dec 01 '15 at 16:27
  • Thank you so much! I am trying to award you a bounty for this solution but SO is saying I have to wait 23 hours for some reason. – Joel Farris Dec 01 '15 at 21:05
  • A related question, do you think it is possible to detect when the user has disengaged with the scroll controller? (On a Macbook trackpad that would be when they have removed one of the two fingers that were initiating the scroll.) Safari’s implementation of the scroll snap points CSS feature seems to base “when to start the snapping” off of that. – Joel Farris Dec 01 '15 at 21:09
  • Sorry for being late in my answer - I have had other things on my mind. But no, unfortunately not :/ There is some built in support for touch events in jquery as seen in this thread: (http://stackoverflow.com/questions/4755505/how-to-recognize-touch-events-using-jquery-in-safari-for-ipad-is-it-possible) - but I do not think it will work for touchpad-touches. Good lyck with making a great snapping solution :) – woobione Dec 11 '15 at 14:24
  • No worries, again thanks so much! You can see your answer in action here: http://trailer.town/2016/zoolander-2/trailer-1 – Joel Farris Dec 11 '15 at 18:06
  • Great job and great design! It's fun to see what the code is used for :D – woobione Dec 22 '15 at 13:19
3

All right, there are couple of things you gonna have to deal with to get a good result: which are performance, call stack queue, easing.

Performance wise you should drop jQuery animate and use VelocityJs which gives a smoother transition, better frame per second (fps) to avoid screen glitches especially on mobiles.

Call stack: you should wrap whatever logic you have to animate the scrolltop with 'debounce' function, set the delay for let say 500mm and check the scrolling behavior. Just so you know, the 'scroll' listener your using is firing on each pixel change and your script will go crazy and erratic. (It is just gonna be a moment of so many calc at the same time. Debounce will fix that for you)

Easing: make the transition looks cool not just dry snappy movement.

Remember, 'easing' with Velocity starts with 'mina.' i.e.

'Mina.easingFnName'

Finally, your logic could be right, i am in my phone now cannot debug it but try to simplify it and work with a single problem at once, be like i.e.

If ( top > 380 )  // debounce(...)
KQI
  • 322
  • 1
  • 16
  • 1
    Dont forget to add debounce fn before you load the anim script – KQI Nov 29 '15 at 21:36
  • Thanks for the suggestions. I definitely want to check out VelocityJS but I’ve been sticking with mostly CSS transitions for this project so far. I want to try using a debounce function but I’m not familiar with them. By any chance could you point me toward a good example of one? Thanks! – Joel Farris Nov 29 '15 at 22:31
  • Sure, try this out: http://tinyurl.com/odvpj6f notice debounce always returns a function so to run the debounced function pass additional braces '()'. i.e. debounce(function () { ... }, 400)(); – KQI Dec 23 '15 at 07:02