Isaac Lubow
Isaac Lubow

Reputation: 3573

How can I "snap" scroll to the nearest predefined position?

I have a website that is essentially four divs - each of which is set to the height of the window so that the total document is four times the height of the window.

The idea is that a click on a div advances the scroll by one "window height" - which works fine, like this:

// on click event

if(cur_frame<number_slides){
   scrolling = true;
   $('html,body').animate({scrollTop:window_height*cur_frame},function(){
      scrolling=false;
   });
}

After the user scrolls the page manually, however, I'd like to "snap" the position to the nearest multiple of the window height - so a given div is once again centered on the screen. I tried using a timeout, figuring that a small delay would keep it from triggering a thousand times a second...

// on scroll event

clearTimeout(scroll_timer);
if(!scrolling) scroll_timer = setTimeout(function(){
   if(cur_scroll!=window_height*(cur_frame-1)) {
      scrolling = true;
      $('html,body').stop().animate({scrollTop:window_height*(cur_frame-1)},function(){
         scrolling = false;
      });
   }
},100); //20? 400? 1000?

...but couldn't strike a balance between the script fighting the user over scroll position, or a seriously long delay that defeats the "snapping" effect.

Any suggestions how this might be achieved?

Upvotes: 5

Views: 29138

Answers (5)

Unkn0wn0x
Unkn0wn0x

Reputation: 1167

What about using a simple scrollTo? Plain Javascript and CSS used, no frameworks or libraries.

Here are two examples, one for vertical scrolling and the other for horizontal scrolling:

Upvotes: 4

Luca Falasco
Luca Falasco

Reputation: 89

If you want to consider a cross-browser javascript re-implementation of the native CSS Scroll Snap spec, as already answered here: How to emulate CSS Scroll Snap Points in Chrome?, you can use this library:

The main reason to use this instead of the native css solution is that it works in all modern browsers and has a customizable configuration to allow custom timing in transitions and scrolling detection.

The library re-implements the css snapping feature using vanilla javascript easing functions, and works using the values of the container element's scrollTop/scrollLeft properties and the scroll Event Listener

Here is an example that shows how to use it:

import createScrollSnap from 'scroll-snap'

const element = document.getElementById('container')

const { bind, unbind } = createScrollSnap(element, {
  snapDestinationX: '0%',
  snapDestinationY: '90%',
  timeout: 100,
  duration: 300,
  threshold: 0.2,
  snapStop: false,
  easing: easeInOutQuad,
}, () => console.log('snapped'))

// remove the listener 
// unbind();

// re-instantiate the listener 
// bind();

Upvotes: 2

gerwitz
gerwitz

Reputation: 1116

There is a CSS spec for this, and it is well supported with native rendering and very nice touch behavior except on Chrome: http://caniuse.com/#feat=css-snappoints

For the laggard browser, there's a polypill: https://github.com/ckrack/scrollsnap-polyfill

See also How to emulate CSS Scroll Snap Points in Chrome?

Upvotes: 6

Stone
Stone

Reputation: 2668

The jquery scrollsnap plugin for this supports down to IE9.

What you're looking for is called "Scroll Snap".

<script src="demo/foundation/javascripts/jquery.js"></script>
<script src="src/jquery.event.special.js"></script>
<script src="src/jquery.easing.min.js"></script>
<script src="src/jquery.scrollsnap.js"></script>
<script type="text/javascript">
$(document).ready(function() {
    $(document).scrollsnap({
        snaps: '.snap',
        proximity: 50
    });
});
</script>

Upvotes: 6

Damon Smith
Damon Smith

Reputation: 1790

You could do this with javascript or for a slightly simpler and older solution you can use page anchors. If you change your document.location.hash to an anchor that exists in the page then the browser will scroll to it. So in your HTML put some anchors in the page:

<a name="anchor1" id="anchor1"></a>

then in your js put:

document.location.hash = "anchor1";

Upvotes: 0

Related Questions