<div class="btns">
  <button class="btn js-btn1">Scroll to section 1</button>
  <button class="btn js-btn2">Scroll to section 2</button>
  <button class="btn js-btn3">Scroll to section 3</button>
  <button class="btn js-btn500">Scroll to position 500px</button>
  <button class="btn js-btn50000">Scroll to position 50000px (on purpose longer than document)</button>
</div>

<div class="section js-section1">JS - Section 1</div>
<div class="section js-section2">JS - Section 2</div>
<div class="section js-section3">JS - Section 3</div>
@import "https://fonts.googleapis.com/css?family=Pacifico";

* {
  margin: 0;
  box-sizing: border-box;
}

.btns {
  position: fixed;
  top: 1rem;
  left: 1rem;
  right: 1rem;
  text-align: center;
  display: flex;
  justify-content: center;
  
}

.section {
  width: 100%;
  height: 100vh;
  line-height: 100vh;
  text-align: center;
  background-size: cover;
  font-size: 5vw;
  text-shadow: 1px 1px 1px rgba(255,255,255,.25);
  
  &.js-section1 {
    background-image: url(https://images.pexels.com/photos/895228/pexels-photo-895228.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260);
  }
  
  &.js-section2 {
    background-image: url(https://images.pexels.com/photos/551579/pexels-photo-551579.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260);
  }
  &.js-section3{
    background-image: url(https://images.pexels.com/photos/569170/pexels-photo-569170.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260)
  } 
}

.btn{
	position: relative;
	padding: 10px 20px;
  margin: 0 5px;
  float: left;
	border-radius: 10px;
	font-family: 'Pacifico', cursive;
  font-size: 18px;
	color: #FFF;
	text-decoration: none;	
	transition: all 0.1s;
	background-color: #3498DB;
	border-bottom: 5px solid #2980B9;
	text-shadow: 0px -2px #2980B9;
}

.btn:active{
	transform: translate(0px,5px);
	border-bottom: 1px solid;
}
// Browser support:

// Chrome >= 24
// Firefox >= 23
// IE >= 10
// Opera >= 15
// Safari >= 8 (on previous versions it breaks on 'now' in window.performance)
// Android 4.4
// Firefox >= 23
// IE Mobile >= 10
// Opera Mobile >= 15
// Safari iOS >= 9
// Chrome for Android >= 35

/**
 *
 * @param {(number|HTMLElement)} destination - Destination to scroll to (DOM element or number)
 * @param {number} duration - Duration of scrolling animation
 * @param {string} easing - Timing function name (Allowed values: 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic', 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint', 'easeOutQuint', 'easeInOutQuint')
 * @param {function} callback - Optional callback invoked after animation
 */
function scrollIt(destination, duration = 200, easing = 'linear', callback) {

  // Predefine list of available timing functions
  // If you need more, tween js is full of great examples
  // https://github.com/tweenjs/tween.js/blob/master/src/Tween.js#L421-L737
  const easings = {
    linear(t) {
      return t;
    },
    easeInQuad(t) {
      return t * t;
    },
    easeOutQuad(t) {
      return t * (2 - t);
    },
    easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    },
    easeInCubic(t) {
      return t * t * t;
    },
    easeOutCubic(t) {
      return (--t) * t * t + 1;
    },
    easeInOutCubic(t) {
      return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
    },
    easeInQuart(t) {
      return t * t * t * t;
    },
    easeOutQuart(t) {
      return 1 - (--t) * t * t * t;
    },
    easeInOutQuart(t) {
      return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
    },
    easeInQuint(t) {
      return t * t * t * t * t;
    },
    easeOutQuint(t) {
      return 1 + (--t) * t * t * t * t;
    },
    easeInOutQuint(t) {
      return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
    }
  };


  // Store initial position of a window and time
  // If performance is not available in your browser
  // It will fallback to new Date().getTime() - thanks IE < 10
  const start = window.pageYOffset;
  const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
  // const startTime = typeof(window.performance['now']) == 'function' ? performance.now() : new Date().getTime();


  // Take height of window and document to sesolve max scrollable value
  // Prevent requestAnimationFrame() from scrolling below maximum scollable value
  // Resolve destination type (node or number)
  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 is not supported
  // Move window to destination position and trigger callback function
  if ('requestAnimationFrame' in window === false) {
    window.scroll(0, destinationOffsetToScroll);
    if (callback) {
      callback();
    }
    return;
  }


  // function resolves position of a window and moves to exact amount of pixels
  // Resolved by calculating delta and timing function choosen by user
  function scroll() {
    const now = 'now' in window.performance ? performance.now() : new Date().getTime();
    const time = Math.min(1, ((now - startTime) / duration));
    const timeFunction = easings[easing](time);
    window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));

    // Stop requesting animation when window reached its destination
    // And run a callback function
    if (window.pageYOffset === destinationOffsetToScroll) {
      if (callback) {
        callback();
      }
      return;
    }

    // If window still needs to scroll to reach destination
    // Request another scroll invokation
    requestAnimationFrame(scroll);
  }


  // Invoke scroll and sequential requestAnimationFrame
  scroll();
}

// Scroll to section 1
document.querySelector('.js-btn1').addEventListener('click', () => {
  scrollIt(
    document.querySelector('.js-section1'),
    300,
    'easeOutQuad',
    () => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
  );
});

// Scroll to section 2
document.querySelector('.js-btn2').addEventListener('click', () => {
  scrollIt(
    document.querySelector('.js-section2'),
    300,
    'easeOutQuad',
    () => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
  );
});

// Scroll to section 3
document.querySelector('.js-btn3').addEventListener('click', () => {
  scrollIt(
    document.querySelector('.js-section3'),
    300,
    'easeOutQuad',
    () => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
  );
});

// Scroll to 500px from top
document.querySelector('.js-btn500').addEventListener('click', () => {
  scrollIt(
    500,
    300,
    'easeOutQuad',
    () => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
  );
});

// Scroll to 50000px from top (on purpose longer than document)
document.querySelector('.js-btn50000').addEventListener('click', () => {
  scrollIt(
    50000,
    300,
    'easeOutQuad',
    () => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
  );
});

View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.