Add a Debounced Scroll-To-Top Button to Your Site

Published: 2024-05-06 | Updated: 2024-05-16

It only takes a few lines of code to add an unobtrusive button to your site that, when clicked, takes visitors back up to the top of the page. The button shouldn’t just always be there, however. There are some rules to follow about when it should make itself available.

A tall order? Really not so bad. Here’s the code to make it happen.

First, some markup. It’s as simple as it gets.

<button id=scrollToTopButton>&uarr;</button>

and here’s what the code above, our button, will look like in the browser

Now let’s give it some style by adding some CSS

#scrollToTopButton {
 background: none;
 font-size: 2em;
 border: 2px solid darkorange;
 color: darkorange;
 width: min-content;
 padding: 0 0.25em;
 cursor: pointer;
 /* position it near the bottom of the screen
  where it's easy to get to on mobile and generally
  out of the way 
 position: fixed;
 bottom: 1em;
 right: 1em;
 /* make sure it sits on top of other content */
 z-index: 100;
/* set it up to move into and out of view and 
  to fade in and out as needed. Start with the 
  button hidden off screen and transparent 
 opacity: 0;
 transform: translateY(100px);
 transition: all 0.5s ease;

/* The CSS needed when we want the button to show */ {
 opacity: 0.8;
 transform: translateY(0);

And finally, this bit of Javascript will watch for window scroll events and decide whether to apply the show class to our #scrollToTopButton based on how far the visitor has scrolled and in which direction they are currently headed.

 let previousScrollPosition = window.scrollY,
  timeout = 0,
  debounceThreshold = 150,
  scrollThreshold = 150,
  scrollToTopBtn = document.getElementById("scrollToTopButton"),
  rootElement = document.documentElement;

 // smoothly scroll to the top of the browser window
 function scrollToTop() {
  // Scroll to top logic
   top: 0,
   behavior: "smooth"

  if (scrollToTopBtn !== null) scrollToTopBtn.addEventListener("click", scrollToTop);

 window.onscroll = () => {
 // don't bother with any of this if there is no button
  if (scrollToTopBtn === null) return
 // clear any previous timeout
  if (timeout !== 0) clearTimeout(timeout);
 // then set a new one
  timeout = setTimeout(() => {
   const currentScrollPosition = window.scrollY,
    scrollingDown = currentScrollPosition > previousScrollPosition,
    scrollingUp = !scrollingDown,
    isTimeToShow = currentScrollPosition > scrollThreshold;

   previousScrollPosition = currentScrollPosition;

   if (scrollingDown) scrollToTopBtn.classList.remove('show');
   else if (isTimeToShow) scrollToTopBtn.classList.add('show');
   else scrollToTopBtn.classList.remove('show')

  }, debounceThreshold);