I want to make a one pager website, without using any third party libraries, like FullPage.js.
- when scroll starts –> instead of waiting for the end of the natural scrolling, I want it to take no effect (so no visible scroll caused by the mouse) and to run my code instead. (so it could always go to next section, or previous one, without relying on the amount of the users scroll)
Do you have any idea how could I achieve this? My code snippet waits for the end of scroll, and then jumps to where it should, so it’s not working as intended.
(the first section has a “current” class and then the code snippet works by manipulating the 100vh sections by adding/removing this class)
You can see the code snippet I am using below or here: https://codepen.io/makiwara/pen/PoqjdNZ
Thank you very much for your help, have a nice day!
var script = document.createElement('script');script.src = "https://code.jquery.com/jquery-3.4.1.min.js";document.getElementsByTagName('head')[0].appendChild(script); var timerId; var scrollableElement = document.body; //document.getElementById('scrollableElement'); scrollableElement.addEventListener('wheel', checkScrollDirection); function checkScrollDirection(event) { var $current = $('.current'); if (checkScrollDirectionIsUp(event)) { console.log('UP'); $prev = $current.prev(); if ($prev.length) { clearTimeout(timerId); timerId = setTimeout(function(){ $current.removeClass('current'); $prev.addClass('current'); $('body,html').animate({ scrollTop: $('.current').offset().top }, 100); }, 100) } } else { console.log('Down'); $next = $current.next(); if ($next.length) { clearTimeout(timerId); timerId = setTimeout(function(){ $current.removeClass('current'); $next.addClass('current'); $('body,html').animate({ scrollTop: $('.current').offset().top }, 100); } , 100) } } } function checkScrollDirectionIsUp(event) { if (event.wheelDelta) { return event.wheelDelta > 0; } return event.deltaY < 0; }
Advertisement
Answer
What you need is throttling the event listener, i.e. limit a function call only once per a time period.
What your code is doing is essentially debouncing i.e limit a function call only after a wait time period has passed.
Firstly ditch the timers you’re using. You need to somehow block scrolling from happening more than once. The JavaScript part can be easy if you use Underscore.js’s throttle function with one caveat though: It passes through subsequent events after the time period has passed. Luckily, its debouncing method accepts a third argument that gives the behavior you’d want:
scrollableElement.addEventListener( "wheel", _.debounce(checkScrollDirection, 200, true) // immediately call the function _once_ );
This third argument makes the debounced function behave like a throttled one, that is it will fire only once and at the same time it will fire immediately.
So assuming that your event handler is now free from the original timeout
function checkScrollDirection(event) { var $current = $(".current"); if (checkScrollDirectionIsUp(event)) { console.log("UP"); $prev = $current.prev("section"); if ($prev.length) { $current.removeClass("current"); $prev.addClass("current"); $("body,html").animate( { scrollTop: $prev.offset().top }, 100 ); } } else { console.log("Down"); $next = $current.next("section"); if ($next.length) { $current.removeClass("current"); $next.addClass("current"); $("body,html").animate( { scrollTop: $next.offset().top }, 100 ); } } }
btw, try to get into the habit of specifying selectors inside .next()
and .prev()
since jQuery will match all possible siblings, which most likely you don’t want. In this case, codepen appends additional <script>
elements and jQuery will match those as well.
Now if you try this, you’ll notice that the window still responds to every scroll event. Scroll events are one of those events that cannot be cancelled so you need to disable it via CSS
The easiest way is to hide the overflow of the body
body { max-height: 100vh; overflow: hidden; }
And that’s it. You may need to adjust the throttle waiting time period to match your preferences.
You can find a working version of the codepen here: https://codepen.io/vassiliskrikonis/pen/XWbgxLj