Search code examples
javascriptscrollscrollview

Wheel scroll issue: How to set scrolling so that only one section moves at a time?


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>


Solution

  • 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>