Back To Top Button: A Few Ways

A solution for a Back To Top button can be done a few ways, including different JavaScript methods and at least one CSS solution.


A lit up sign indicates the elevator is going up

Posted January 9, 2021
Filed Under: blog, featured


I’m building a site that has long pages of content (especially on mobile), and I thought it’d be good to have a button to bring you back to the top of the page – a Back to Top Button. I’ve done this before, using JavaScript to listen for the scroll event and toggle the button’s class to bring it in to view after a threshold is reached. Of course, this is resource intensive, since the event handler is being triggered on every scroll event. I wanted to find a way to make it less resource intensive, and I think I’ve found the best solution.


With JavaScript

With JavaScript, its fairly simple: add an event listener for the scroll event and toggle the button’s class to display it when the user has scrolled past the threshold (and do the opposite below the threshold). Here’s the markup, followed by the JavaScript. Note that the “bunch of content” has a height of at least a few pages, and the button element has a fixed position and is not displayed in it’s default classless state. Adding the class of “is-active” makes it display: block


<div>
  <!--a bunch of content-->
</div>
<button id='btt'>^</button>


document.addEventListener('DOMContentLoaded', () => {
  //the button
  const btt = document.getElementById('btt');
  //the scroll listener
  window.addEventListener('scroll', () => {
    //the amount of scroll
    var top = window.pageYOffset;
    //does the button have the class that displays it
    var is_active = btt.classList.contains('is-active');
    if (top > 200 && !is_active) {
      //if the scroll amount is greater than the threshold and the 
      //button isn't displayed, add the class to display it
      btt.classList.add('is-active');
    } else if (top < 200 && is_active) {
      //if the scroll amount is less than the threshold and the 
      //button is displayed, remove the class that displays it
      btt.classList.remove('is-active');
    }
  }, {passive: true})
  //passive: true makes it faster, it's a little bit of a 
  //rabbit hole to explain here
  
  //the click handler, scrolls the page back to top
  btt.addEventListener('click', (e) => {
    e.preventDefault();
    window.scroll({
      top: 0,
      behavior: 'smooth'
    });
  })
})

Here’s that in action:

See the Pen
Smooth Sliding Back To Top Button
by Nate Northway (@the_Northway)
on CodePen.

The problem with the above code is that it fires every time a user scrolls, for every pixel. It’s expensive to run. Maybe not when this is the only thing happening, but couple this with all the other things on a website and it starts getting heavy, fast. I wanted to see what I could do to make this less resource intensive, and found a solution that kind of works, but is still resource intensive.


Using setTimeout()

We could use the JavaScript setTimeout() function to check the scroll position every “x” ms. The benefit here is that it will only check scrolling status every so often, saving *some* overhead. Here’s that:


document.addEventListener('DOMContentLoaded', () => {
  //set the default state
  let scrolling = false;
  //the button
  const btt = document.getElementById('btt');
  //change the default state on scroll
  window.addEventListener('scroll', () => {
    scrolling = true;
  })
  //run event handler every 300 ms
  setInterval(() => {
    if (scrolling) {
      //reset the state var
      scrolling = false;
/*in here goes the code from the scroll listener in the above code block*/
    }
  }, 300)
})

So that’s better, but there are downsides: it still isn’t the best, and you can sense a slight lag. Granted, not as resource intensive, but it still runs a function on every scroll (a light one, but the event handler is still using a resource) and now it also introduces a second function run every 300ms.
This method is known as event throttling, which sounds nice, but effectively does the same thing the passive: true parameter passed to the original event handler did. Here’s this method in action (notice the lag in the appearance/hiding of the button):

See the Pen
Smooth Sliding Back To Top Button (but a little better)
by Nate Northway (@the_Northway)
on CodePen.


So, what now?

Forget about doing this in JS. CSS has a way to show and hide things when needed. I was able to accomplish this same effect by changing the markup a bit and using position: sticky; on the button element. I had to change up the markup a little bit and do a little trickery with the positioning, but it works better than the second JavaScript method and almost as good as the first JS method, but has zero overhead attached.


<div>
<button id='btt>^</button>
<!--a whole bunch of content-->
</div>


#btt {
  position: sticky;
  top: calc(100% - 4em);
  left: calc(100% - 4em);
}

Of course, we still need to attach a handler for the button click in JS, but that’s fine. Here is the above code, in action. You’ll notice a few extras here like a hero and footer sections to better demonstrate what’s happening.

See the Pen
Smooth Sliding Back To Top Button (without scroll listener)
by Nate Northway (@the_Northway)
on CodePen.

0 Comments


Leave a Reply

Your email address will not be published. Required fields are marked *