Search code examples
htmlcssscrollsmooth-scrolling

Smooth scroll, snappy scroll and anchor navigation at the same time


I was trying to achieve smooth and snappy scrolling while having anchor navigation at an HTML component. Only one "tab" should be visible at a time. The user would have to click in the tab name in order to change the current "tab".

The problem I am facing is that I can not have both features at the same time. If the scrolling is snappy, then the smooth animation is not carried out and vice versa.

I will also want to make that the user can drag the tab content in order to change the current tab, but this is for another question.

Code: (Uses tailwind, but it is not relevant)

<script src="https://cdn.tailwindcss.com/3.4.5"></script>
<html lang="en-GB">

<body>
  <main>
    <div class="flex flex-row gap-2 bg-red-500">
      <a aria-label="Lorem ipsum" href="#lorem-ipsum">Lorem ipsum</a>
      <a aria-label="Amet porttitor" href="#amet-porttitor">Amet porttitor</a>
      <a aria-label="Blandit turpis" href="#blandit-turpis">Blandit turpis</a>
    </div>

    <div class="flex flex-row snap-x snap-mandatory overflow-x-auto overflow-y-auto max-h-[100vh]">
      <div class="min-w-[100vw] min-h-[80vh] snap-start">
        <p id="lorem-ipsum">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit adipiscing bibendum est ultricies integer quis.
        </p>
      </div>
      <div class="min-w-[100vw] min-h-[80vh] snap-start">
        <p id="amet-porttitor">
          Amet porttitor eget dolor morbi. Ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Cras tincidunt lobortis feugiat vivamus at. Eleifend donec pretium vulputate sapien
        </p>
      </div>
      <div class="min-w-[100vw] min-h-[80vh] snap-start">
        <p id="blandit-turpis">
          Blandit turpis cursus in hac habitasse platea. Egestas tellus rutrum tellus pellentesque eu. In eu s augue neque gravida. Tristique nulla aliquet enim tortor at auctor. A
        </p>
      </div>
    </div>
  </main>
  <style is:global>
    html {
      scroll-behavior: smooth;
    }
    
    @media (prefers-reduced-motion: reduce) {
      html {
        scroll-behavior: auto;
      }
    }
  </style>
</body>

</html>


Solution

  • With a bit of JavaScript, it's possible:

    document.querySelectorAll('main > div a').forEach(anchor => {
      anchor.addEventListener('click', function(e) {
        e.preventDefault(); // Prevent default anchor click behavior
    
        const targetId = this.getAttribute('href').substring(1); // Get the target ID
        const targetElement = document.getElementById(targetId);
    
        if (targetElement) {
          targetElement.scrollIntoView({
            behavior: 'smooth',
            block: 'start',
            inline: 'nearest'
          });
        }
      });
    });
    <script src="https://cdn.tailwindcss.com/3.4.5"></script>
    <html lang="en-GB">
    
    <body>
      <main>
        <div class="flex flex-row gap-2 bg-red-500">
          <a aria-label="Lorem ipsum" href="#lorem-ipsum">Lorem ipsum</a>
          <a aria-label="Amet porttitor" href="#amet-porttitor">Amet porttitor</a>
          <a aria-label="Blandit turpis" href="#blandit-turpis">Blandit turpis</a>
        </div>
    
        <div class="flex flex-row snap-x snap-mandatory overflow-x-auto overflow-y-auto max-h-[100vh]">
          <div class="min-w-[100vw] min-h-[80vh] snap-start">
            <p id="lorem-ipsum">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit adipiscing bibendum est ultricies integer quis.
            </p>
          </div>
          <div class="min-w-[100vw] min-h-[80vh] snap-start">
            <p id="amet-porttitor">
              Amet porttitor eget dolor morbi. Ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Cras tincidunt lobortis feugiat vivamus at. Eleifend donec pretium vulputate sapien
            </p>
          </div>
          <div class="min-w-[100vw] min-h-[80vh] snap-start">
            <p id="blandit-turpis">
              Blandit turpis cursus in hac habitasse platea. Egestas tellus rutrum tellus pellentesque eu. In eu s augue neque gravida. Tristique nulla aliquet enim tortor at auctor. A
            </p>
          </div>
        </div>
      </main>
      <style is:global>
        html {
          scroll-behavior: smooth;
        }
        
        @media (prefers-reduced-motion: reduce) {
          html {
            scroll-behavior: auto;
          }
        }
      </style>
    </body>
    
    </html>