A button to return to the top of the page allows the user to quickly return to the top of the page without making too much effort. This can be very useful when the page has a lot of content or which happens, for example, on one page websites, when infinite scrolling is used, or on mobile devices where different screen sizes can cause the content to scroll extend.
Those buttons usually float in the bottom corner of sites and then take you back to the top of the page when clicked. They are pretty easy to create with JavaScript. But visually, we are looking for it to be non-obtrusive while still being a large enough target to tap or click. Let’s look at a few ways we can do this, starting simple, then improving things as we go.
Option 1: Keep it simple
First, we select the button in JavaScript.
var scrollToTopBtn = document.getElementById("scrollToTopBtn")
Now document.documentElement
returns the root element of the document. We need it to get the offset values. So, next let’s save it in a variable called rootElement
— that way it’s easier to call in the code.
var rootElement = document.documentElement
We’ll add a click event listener to the button:
function scrollToTop {
// scroll to top logic
}
scrollToTopBtn.addEventListener("click", scrollToTop)
Then, inside the scrollToTop
function, we will make it scroll to the top of the screen with the scrollTo
method.
function scrollToTop() {
// Scroll to top logic
rootElement.scrollTo({
top: 0,
behavior: "smooth"
})
}
We can style the button up a bit as well:
#scrollToTopBtn {
background-color: black;
border: none;
border-radius: 50%;
color: white;
cursor: pointer;
font-size: 16px;
line-height: 48px;
width: 48px;
}
Now we’re able to drop the button somewhere down the page, say, the footer:
<footer>
<!-- Scroll to top button -->
<button id="scrollToTopBtn">☝️</button>
</footer>
And we get this:
Option 2: Detecting the scroll position
We can detect scrolling with a scroll event listener.
function handleScroll() {
// Do something on scroll
}
document.addEventListener("scroll", handleScroll)
The handleScroll
function will be called every time the user scrolls. Now we need the total number of pixels we can scroll.
scrollHeight
gives the height of an element, including the part not visible due to overflow.clientHeight
gives the inner height of an element in pixels, which is the height of the visible part.
If we subtract scrollHeight
by clientHeight
, we get the total amount of pixels that we can scroll:
var scrollTotal = rootElement.scrollHeight - rootElement.clientHeight
Now we have a variable called scrollTotal
that represents the maximum number of pixels that can be scrolled vertically. By dividing the amount scrolled by the total amount of pixels we can scroll, we get a ratio between 0 and 1. Playing with this ratio, we can easily toggle the button on and off.
For example, we will add a condition that shows the scroll-to-top button when the user has scrolled 80%, (or a ratio of 0.80) down the total height of the page. 80% is an arbitrary number. Basically, the closer we get to 1, the more the user has to scroll before seeing the button.
Here’s the JavaScript:
var rootElement = document.documentElement
function handleScroll() {
// Do something on scroll
var scrollTotal = rootElement.scrollHeight - rootElement.clientHeight
if ((rootElement.scrollTop / scrollTotal ) > 0.80 ) {
// Show button
scrollToTopBtn.classList.add("showBtn")
} else {
// Hide button
scrollToTopBtn.classList.remove("showBtn")
}
}
document.addEventListener("scroll", handleScroll)
We’re going to want some CSS to position the button correctly when it comes into view:
.scrollToTopBtn {
/* same general styles as before */
/* place it at the bottom-right corner */
position: fixed;
bottom: 30px;
right: 30px;
/* keep it at the top of everything else */
z-index: 100;
/* hide with opacity */
opacity: 0;
/* also add a translate effect */
transform: translateY(100px);
/* and a transition */
transition: all .5s ease
}
.showBtn {
opacity: 1;
transform: translateY(0)
}
With that, the button appears when the user gets 80% down the page and then hides when it’s higher than that.
This seems like a grand option, and setting an event listener to do this is pretty easy. But the performance overhead can be costly since we’re always checking the current scroll position.
There’s another option that takes care of this…
Option 3: Intersection Observer
The Intersection Observer API is an excellent solution to the above problem. It’s a fairly recent browser API that lets developers hand most of these tasks off to the browser, in a way that is more optimized. Travis Almand wrote up a thorough explanation of how it works. Here’s how MDN defines it:
The Intersection Observer API provides a way to asynchronously observe changes for the intersection of a target element with an ancestor element or with a top-level document’s viewport.
Pretty neat! That means the button can be our target element:
// We select the element we want to target
var target = document.querySelector("footer");
We then write a callback function that does something when our element becomes “intersects” with the viewport — which is a fancy way of saying when it comes into view.
And once the footer enters or leaves the viewport, all we really want to do is add or remove a class. The callback receives an array of entries as a parameter.
function callback(entries, observer) {
// The callback will return an array of entries, even if you are only observing a single item
entries.forEach(entry => {
if (entry.isIntersecting) {
// Show button
scrollToTopBtn.classList.add('showBtn')
} else {
// Hide button
scrollToTopBtn.classList.remove('showBtn')
}
});
}
We need to create a new IntersectionObserver
instance and pass it the callback function we just wrote.
let observer = new IntersectionObserver(callback);
Finally, we tell the observer to start watching (err, observing) the target element that was selected above for when it intersects with the viewport:
observer.observe(target);
And what about smooth scrolling?
Of course it’s possible! In fact, Chris showed us how it can be done with CSS back in 2019:
<html id="top">
<body>
<!-- the entire document -->
<a href="#top">Jump to top of page</a>
</body>
</html>
html {
scroll-behavior: smooth;
}
There’s a little more nuance to there, like accessibility enhancements that Chris also covers in the post. The point is that CSS is gaining new powers that can accomplish things that we used to use JavaScript for.
There you have it! We started with a pretty simple idea. We enhanced it by displaying and hiding the button based on the user’s scroll position. Then we improved the performance by implementing the Intersection Observer API instead of watching the current scroll position. And, finally, we saw how CSS can be used for smooth scrolling. All together, we get a scroll-to-top button that is easy to see and use, while not blocking other elements on the page.
Please use throttling for the scroll-event implementation.
Thanks
Personaly i think that a scroll up button should be show directly when a user start to scroll up
Second that!
All of those examples are missing an interruption trigger. Users input should always have higher priority. If system starts scrolling automatically I should be able to interrupt the scrolling by using my mouse wheel or trackpad gesture, or touch the screen. And after the scroll event has finished, the system shouldn’t continue scrolling up – so users aren’t fighting with the system.
I’ve seen this done badly on so many sites.
Nice one, but you can make it nicer by not waiting for the user to scroll to the extreme bottom before the button appears, this would be better for real long pages where the user had had enough rolls on the track to even getting to the middle of the page.
Are there any stats about the usage of all the “scroll to top button”s in the world?
I am just curious. I rarely use them and could live without them.
I see that it is a good small programming practice but does it add to the overall experience?
Do you use them?
And please don’t forget about the accessibility, right now your button is focusable without label while offscreen. To hide it from a screenreader you should add visibility: hidden, when it is offscreen. You can transition visibility, work with transition delay to have the button visible until it fades out. Or have it visible on focus.
Add an aria-label to give it some meaning.
I never used them as well :D I would be interested in that as well.
I think we could modify opacity of the button in order to make it visible gradually while we scroll down. Having it set just to appear only when we get closer to the footer seems like we could just put it in the footer (with no JS needed, just a simple link). However, users need to see that such button is available to them, not only after scrolling up to 80% of the page length.
Why can’t the button appear as soon as one starts scrolling down, like once the scroll page is longer than 100vh.
Thanks. Very good article. I’d suggest including a CSS-only alternative, such as the one provided by this pen:
– Pure CSS Scroll To Top
I’v never noticed someone using such a button.
IMHO nearly everyone just uses the Home/Pos1-Key…
A button to return to the top of the page allows the user to quickly return to the top of the page without making too much effort. This can be very useful when the page has a lot of content.
It would be great if this could also work in IE. Anybody knows some walkaround for it?
How to show back to top only when starting scrolling up at some point? I cant find any examples
Nice. But:
It would be nicer, if the “Scroll to Top Button” appears when the user starts scrolling. Simple solution: Change the target in your snippet. Like here:
I really hope this isn’t a double post now, if so, please delete!:
How about making the Scroll-to-Top Button appear when the headline disappears? Example:
Any way to do exactly this but make the button appear when you’re a certain number of pixels below the heading? For example, 500px below the heading?
I don’t want it showing up too soon.