JSFiddle - React, Tailwind, and code Playground

by Константин Дралюк

HTML

<div class="long" id="b1" style="background: yellow;">
  <a href="#b1">b2</a>
  <a href="#b2">b2</a>
  <a href="#b3">b3</a>
  <a href="#b4">b4</a>
  <a href="#b5">b5</a>
</div>
<div class="long" id="b2" style="background: green;"></div>
<div class="long" id="b3" style="background: red;"></div>
<div class="long" id="b4" style="background: gray;"></div>
<div class="long" id="b5" style="background: violet;"></div>

CSS

body {
  margin: 0;
}

.long {
  height: 1000px;
}

JavaScript

function scrollIt(destination, duration = 200, callback) {

  const easeInOutCubic = t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;

  const start = window.pageYOffset;
  const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();

  const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
  const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;
  const destinationOffset = typeof destination === 'number' ? destination : destination.offsetTop;
  const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);

  if ('requestAnimationFrame' in window === false) {
    window.scroll(0, destinationOffsetToScroll);
    if (callback) {
      callback();
    }
    return;
  }

  function scroll() {
    const now = 'now' in window.performance ? performance.now() : new Date().getTime();
    const time = Math.min(1, ((now - startTime) / duration));
    const timeFunction = easeInOutCubic(time);
    window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));

    if (window.pageYOffset === destinationOffsetToScroll) {
      if (callback) {
        callback();
      }
      return;
    }

    requestAnimationFrame(scroll);
  }

  scroll();
}

document.querySelectorAll('a[href^="#"]').forEach($anchor => {
	const href = $anchor.getAttribute('href')
  const $target = document.querySelector(href)
  
  if ($target) {
  	$anchor.addEventListener('click', e => {
      scrollIt($target.offsetTop, 800)
      e.preventDefault()
    })
  }
})