Snap scrolling is like continuing a partially executed scroll of an element to the closest start or end of an element(depending what argument did you specify) “If you started something you should finish, or the browser will finish it for you!” That would be the motto behind all this.

In the above case the HTML looks like this:

We have a main tag with sections inside. Each section will have the height of the viewport and when the main tag is scrolled the position of the scroll will always jump/snap to a fix position, like the top of the closest element.

The scroll-snap-type: y mandatory; will specify that the snap on the scroll will happen on Y axis(scrolling vertically) and on the sections the scroll-snap-align: start will snap to the start of the closest element. If the start of the actual element is closer to the scroll point then the snap will be to the same element not the next one or previous one. This means the scroll will just scroll back to the position where you started the scroll.

The mandatory value in the specs is defined like this:

The visual viewport of this scroll container will rest on a snap point if it isn’t currently scrolled. That means it snaps on that point when the scroll action finished, if possible. If content is added, moved, deleted or resized the scroll offset will be adjusted to maintain the resting on that snap point.

The other option for this strictness values would be the promixity instead of mandatory. When using this value the snap will occur only after the user stopped scrolling and the scrollbar position is in the vicinity of a snap point like the start or end of an element, otherwise it remains in that no-man’s-land position where the user left it.

The browser support is pretty good:

For the browsers that don’t yet support it we could use javascript. Also for those of you who don’t like this behaviour where the user need to stop scrolling and only then the browser will correct the scroll position, you need also javascript solution. Also if yo want to scroll without the scrollbar, guess what? You need a javascript solution:

The CSS for the main tag will look like this:

The Javascript will be longer:

Let me explain:

  • We set up 2 event listeners, one for the mouse wheel scroll and the second one for general scrolling
  • We prevent the normal behavior of scrolling when we use the mouse wheel and we add our custom scrolling. element.scrollBy(0, wHeight) means that the main scroll position will be increased with 1 window height. This is dependent of the direction of the scroll wheel. If it was scrolled up than deltaY is smaller then 0 and it will be scrolled to a negative height of the window, if down than it is bigger then 0 and the scrolled value will be the positive window height value.
  • The rest of the code is only to prevent scrolling again while it is tries to snap to a snap position. By default isScrolling variable is false but on the scroll event we set it to true but how can we now when did the user stop scrolling? Well, we set up a timed out operation, with a timeout of 150 milliseconds. While the user scrolls it will never get to assign the isScrolling a false value because it will again and again clear the timeout and start the countdown again. When the user stops, finally the timeout succeeds to execute after 150 milliseconds and the isScrolling will get a false value and the scroll wheel’s if-else condition will be evaluated.

That’s all. I like it better, the way it behaves with javascript. It does not wait the user to stop with the scrolling actually it entirely hijacks the action on wheel scroll and does it’s own custom behaviour and that’s cool.

You can copy the javascript code below:

const wHeight = window.innerHeight;
      const element = document.getElementById('main');
      let isScrolling = false; 
      let scrollingTimeout;

      element.addEventListener('wheel', ev => {
        if(isScrolling) { return; }

        if(event.deltaY < 0) { 
          element.scrollBy(0, -wHeight);
        else {
          element.scrollBy(0, wHeight);

      element.addEventListener('scroll', ev => {
        isScrolling = true;
        scrollingTimeout = setTimeout(() => {
          isScrolling = false;
        }, 150);

Update: If you have a scrollbar on the html also then you need to add some extra “sugar and spice”.


.no-scroll {
   position: fixed; 
   width: 100%

We create a no-scroll class in CSS that we will add on the body later on, when we are scrolling the element(only then) and we take it out when we scrolled our element till the bottom of it.

JS(we change only the scroll event listener function):

element.addEventListener('scroll', ev => {
        isScrolling = true;
        scrollingTimeout = setTimeout(() => {
          isScrolling = false;
        }, 150);

That’s all, happy coding guys !

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Categorized in: