2

I am coding for a scrollable website. It runs very well when I use a mouse, but I am facing an issue when using a laptop scroll pad. When I scroll using the laptop pad, these sections move twice as much, which is not good.

I want to each section to be visible only once during a single scroll.

Only code must be in vanilla javascript.

let sections = document.querySelectorAll(".section")
let sectionsWrap = document.getElementById("sections-wrap")

let currentSectionIndex = 0
let isScrolling = false

let scrollToSection = (index) => {
  sectionsWrap.style.transform = `translateY(-${index * 100}%)`
  sectionsWrap.style.transition = "transform 1s ease-in-out"
}

window.addEventListener("wheel", (event) => {
  if (isScrolling) return

  if (event.deltaY > 0 && currentSectionIndex < sections.length - 1) {
    currentSectionIndex++
  } else if (event.deltaY < 0 && currentSectionIndex > 0) {
    currentSectionIndex--
  } else {
    return
  }

  isScrolling = true
  scrollToSection(currentSectionIndex)

  setTimeout(() => {
    isScrolling = false
  }, 1200)
})
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
}

#sections-wrap {
  width: 100%;
  height: 100%;
  height: 100%;
}

.section {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 2rem;
  color: white;
}

.section:nth-child(1) {
  background: #ff5733;
}

.section:nth-child(2) {
  background: #33ff57;
}

.section:nth-child(3) {
  background: #3357ff;
}

.section:nth-child(4) {
  background: #ff33a1;
}
<div id="sections-wrap">
  <div class="section">
    <h2>Fist Section</h2>
  </div>
  <div class="section">
    <h2>Second Section</h2>
  </div>
  <div class="section">
    <h2>Third Section</h2>
  </div>
  <div class="section">
    <h2>Fourth Section</h2>
  </div>
</div>

4
  • "each section to be visible only once during a single scroll" - and what actually is a "single scroll", by your definition, on a trackpad? Commented Feb 21 at 10:22
  • the wheel event will fire multiple times when triggered by a single gesture (swipe) on the trackpad. You can see yourself adding a console.log in your event handler. Since your debouncing strategy is using a setTimeout with a short interval, there's the chance that the flag gets reset before the chain of events finished coming. The transition animation takes more than such timeout, so you will see two slides if you did a very long gesture. If you rise that number from 1200 to 2000 it could be enough. Commented Feb 21 at 10:31
  • you may also be interested in CSS scroll snap developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll_snap Commented Feb 21 at 10:31
  • @DiegoD scroll-snap runs too fast and has different implementations and timing Chromium vs Firefox. Commented Feb 21 at 13:10

1 Answer 1

1

Your issue happens because touchpads send multiple scroll events quickly, making sections move too much.

I think it can be fixed by Adding a scroll threshold to detect intentional scrolls and prevent overscrolling like what I did here by changing the wheel event handler:

let sections = document.querySelectorAll(".section")
let sectionsWrap = document.getElementById("sections-wrap")

let currentSectionIndex = 0
let isScrolling = false

let scrollToSection = (index) => {
  sectionsWrap.style.transform = `translateY(-${index * 100}%)`
  sectionsWrap.style.transition = "transform 1s ease-in-out"
}

window.addEventListener("wheel", (event) => {
  if (isScrolling || Math.abs(event.deltaY) < 20) return; //leave scrolls

  if (event.deltaY > 0 && currentSectionIndex < sections.length - 1) {
    currentSectionIndex++;
  } else if (event.deltaY < 0 && currentSectionIndex > 0) {
    currentSectionIndex--;
  } else {
    return;
  }

  isScrolling = true;
  scrollToSection(currentSectionIndex);

  setTimeout(() => (isScrolling = false), 1200);
}, {
  passive: false
});
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
}

#sections-wrap {
  width: 100%;
  height: 100%;
  height: 100%;
}

.section {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 2rem;
  color: white;
}

.section:nth-child(1) {
  background: #ff5733;
}

.section:nth-child(2) {
  background: #33ff57;
}

.section:nth-child(3) {
  background: #3357ff;
}

.section:nth-child(4) {
  background: #ff33a1;
}
<div id="sections-wrap">
  <div class="section">
    <h2>Fist Section</h2>
  </div>
  <div class="section">
    <h2>Second Section</h2>
  </div>
  <div class="section">
    <h2>Third Section</h2>
  </div>
  <div class="section">
    <h2>Fourth Section</h2>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.