Add a smart 'back to top' button to your website 👓🔝
Tags: webdev, tutorial, javascript, beginners
Published: 2022-04-27 13:19:23

A "back to top" button helps users navigate to the top of long pages. It is a common UI pattern. You have probably seen it implemented as a floating button with an up arrow, positioned in the bottom right of the page.

I want to do a "smart" version. By this I mean the button is hidden until a user has scrolled down a page AND then scrolls up. I have seen different implementations with some good things and not-so-good things, so I would like to address these with regard to usability and accessibility.

Not totally sure what it is exactly still?

You can see one on [CSS Tricks](https://css-tricks.com/) (as per screenshot below), just scroll down a bit and it will appear.

![css-tricks-example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eeaovzd1fmbz9sjjb836.png)

## Things to consider before adding it to your blog

Before you implement one on your site, you should take a moment to evaluate if you need this based on the design and content of your website.

The [Nielsen Norman Group has some useful User Experience (UX) guidelines](https://www.nngroup.com/articles/back-to-top/) on this.

### Why use it?

When users get further into a really long page, they often will need to get back to the top to:

- See the navigation menu and choose where they want to go next,
- Access sorting and filtering features,
- Make a query in the search box.

If you have navigation at the bottom of the page or you have sticky navigation menus, then you may not need this button. If don't have these features in your layout and you have long content (longer than 4 screens in length), then you can consider adding the button.

### How do we want it to look and behave?

For the appearance, we want the following:

1. Place it in the bottom right corner of the page.
2. Make it big enough to click, but small enough so that it does not block a lot of content.
3. Make it stand out visually by making it a floating button (via shadows).

For the behavior, we will:

1. Delay the appearance until the users scroll down a page AND indicates that they want to up, by scrolling up.
2. We want the scrolling to the top of the page to be smooth; not an instant jump.

There are variations on this, of course. I have included some alternative examples in the [Alternative implementations section](#alternative-implementations) if you would like to do it differently.

## Should I use a link or button?

A good rule of thumb is to: use a link for navigation, and a button for actions.

Since we are navigating to the top of the page, a link is a better fit.

## Is there a CSS-only solution?

Not for my exact requirements.

Termani Afif offers a CSS-only solution [in this article](https://www.freecodecamp.org/news/css-only-back-to-top-button/) (discussed further in [Alternative implementations](#alternative-implementations) section). The button appears after the user scrolls down a fixed amount (one screen plus an offset). The difference with my solution is that the button remains visible beyond that point. If you don't care about hiding the button based on scrolling, then Termani's solution is a better option.

## My solution

In the simplest form, we just need to create a [bookmark link](https://www.w3schools.com/html/html_links_bookmarks.asp). That is where the `href` contains a hash.

We will use an icon to keep the size of the button small. You can use text instead if you prefer.

And then, we need to write a bit of JavaScript to show the button.

Here is the finished article.

{% codepen https://codepen.io/robjoeol/embed/xxpYOKb %}

Now, let's go through the code.

## HTML

I think it is a good idea to place the link at the start of your main content, rather than at the very bottom of the `body`. This gives keyboard users the opportunity to tab to it first. If they need to tab all the way to the last thing in your main content, it kind of defeats the purpose of it!

I use an empty bookmark reference (just a hash) in the `href` of the link that will take us to the root (top) of the document.

I use an icon rather than text inside the link to keep the size small. I choose the "chevron-up" icon with the solid fill from [hero icons](https://heroicons.com/). I copied the SVG and pasted inside the  `a` element. We will set the size of the icon in CSS later.

```html
<html>
 <body>
     <!-- main navigation-->
     
     <a href="#" class="top-link" aria-label="Go to top" aria-hidden="true">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 20 20"
        fill="currentColor"
        role="presentation"
      >
        <path
          fill-rule="evenodd"
          d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z"
          clip-rule="evenodd"
        />
      </svg>
    </a>
     
 	<!-- lot more stuff here-->
  </body>   
</html>
```

We should consider accessibility further. Since the link is hidden by default,  we will add `aria-hidden="true"`, so that it is hidden from assistive technologies also.

Since the link has no text, it will be confusing to screen readers and assistive technologies when the link is shown. You could leave it always hidden with `aria-hidden="true"` if you think that this is something that is not required by assistive technologies. Screen reader users have the ability to jump to major page sections, so they probably do not need it, but I am not sure about others. I think it is good to give everyone the option to use it. So, I will make it fully accessible by adding alternative text to the link through the [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) attribute. I  add [`role="presentation"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/presentation_role) to the SVG icon to remove it from the accessibility tree.

Our JavaScript code will change the value of `aria-hidden` to `true` to show the link.

### CSS

We want the link to be fixed in the bottom-right corner, as this is where people expect to see it. We use `position:fixed` to fix the position along with the `bottom` and `right` properties to place it into the bottom-right. I position a small distance from the corner to stand out well.

To factor in the sizing of our link, we can consult the Web Content Accessibility Guidelines (WCAG) 2.1, which is a standard for making web content more accessible to people with disabilities,. The [guideline for target size](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html) says that our link ("anchor link" in their words) does not need to meet the requirement of being 44 by 44 CSS pixels because a user can scroll. However, I think getting close to that size is a good idea. We fufill this by setting the `height` and `width` of the SVG.

To create a floating effect, we add a subtle offset shadow below the element with `box-shadow`. The values I use below for `box-shadow` is the same as what Material Design uses for its [floating action buttons](https://material.io/components/buttons-floating-action-button).

```css
.top-link {
  position: fixed;
  right: 15px;
  bottom: 15px;

  background-color: rgb(216, 204, 204);
  padding: 0;
  margin: 0;

  border-radius: 50%;
  box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
    0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);

  transition: transform 0.3s ease-in-out;
}

.top-link svg {
  height: 2.25rem;
  width: auto;
}

a[aria-hidden="true"] {
  transform: scale(0);
}

html{
	scroll-behavior: smooth;
}
```

To support accessibility as best as we can, we will use the `aria-hidden` attribute for style the hiding of our link. This way wont forget to add it to the HTML and JS!

We use a [scale transformation](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale) (`transform: scale(0)`) to hide the link. This shrinks the size of the link to zero. We do this, so we can animate it when it is shown and hidden. We add `transition: transform 0.3s ease-in-out;` to the "top-link" class to achieve this.

Finally, we use  [`scroll-behavior: smooth;`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) to ensure when you click the link it scrolls to the top in a smooth fashion. This is applied to the `html` element, so this will be the behaviour for your entire site.

### JavaScript

Without adding any JavaScript. Our link does the job, and looks good. However, the link is there *all* the time, and we do not want that!

The sole mission of this JavaScript code is to control when the link will be visible. We only want the link to appear after the user has scrolled down at least 2 screens, and begins to scroll up.

In a previous post, [How to detect scroll direction in vanilla JavaScript (to make a goofy logo animation)](https://www.roboleary.net/frontend/2022/04/13/detect-scroll-direction-vanilla-javascript.html), I describe how to determine if a user is scrolling up or down. I use a scroll event listener to react to the user's scroll activity. I also discuss how a *throttle* function can improve the performance of the scroll event handling. I will use the `throttle` function from the [lodash library](https://lodash.com/) in this example. If you are not familiar with throttling, give the post a read to understand it fully.

The quick takeaway is that we have a  `isScrollingDown` function that determines if a user is scrolling up or down. We use this function as a condition in our scroll event handler to determine if we should hide or show the link. Our second condition is to only show the link when we have scrolled down at least a single screen, by screen we mean the viewport height. The [`window.innerHeight`](https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight) property is the height of the viewport.

```javascript
let backToTopLink = document.querySelector(".top-link");
let previousScrollPosition = 0;

const isScrollingDown = () => {
  let goingDown = false;

  let scrollPosition = window.pageYOffset;

  if (scrollPosition > previousScrollPosition) {
    goingDown = true;
  }

  previousScrollPosition = scrollPosition;

  return goingDown;
};

const handleScroll = () => {
  let viewportHeight = window.innerHeight;
  let scrollPosition = window.pageYOffset;

  if (isScrollingDown()) {
    backToTopLink.setAttribute("aria-hidden", "true");
  } else if (isScrollingDown() === false && scrollPosition > viewportHeight) {
    backToTopLink.setAttribute("aria-hidden", "false");
  }
};

// the throttle is provided by the lodash library
const scrollThrottle = _.throttle(handleScroll, 100);
window.addEventListener("scroll", scrollThrottle);
```

And that's it. Hopefully, the code is clear enough that you can understand it all!

## Alternative implementations

There are variations on this functionality. I wanted to do it a different way, that's why I wrote my own version! You may like to do it a bit different to me, so I included some alternative examples. I added some comments to discuss some pros and cons of these implementations, and I highlight some issues I found when I went through them.

### [How to Make an Animated Back to Top Button Using Only CSS by Temani Afif  (FreeCode Camp)](https://www.freecodecamp.org/news/css-only-back-to-top-button/)

The author is looking to create a "back to top" button using only CSS. It is a nice solution. The button appears after the user scrolls down a fixed amount (one screen plus an offset). The difference with my solution is that the button remains visible beyond that point.

The animation effects discussed did not work for me in Firefox (Linux), but did in Chrome (Linux). This is because the `mask` property is used in both effects. The `mask` property is not a well implemented CSS feature, the [browser support is odd](https://caniuse.com/?search=mask), unfortunately. So, you should be cautious in using the animations.

In the examples where only an icon is used, accessibility is not considered.

### [How to Make an Unobtrusive Scroll-to-Top Button by Marcel Rojas (CSS Tricks)](https://css-tricks.com/how-to-make-an-unobtrusive-scroll-to-top-button/)

The author is looking to make a button that is "non-obtrusive while still being a large enough target to tap or click." The author places the "go to top" `button` in the `footer` of the website, and uses a `button` with the  emoji "👆"as the text content. The button has no `aria-label`, which is not good for accessibility.

Two different implementation are demonstrated:

1. A scroll event listener is used to show the button when the user scrolls down 80% of the page height. This code is not optimized and the author suggests using the second method instead. To optimize the code you would need to consider [throttling the number of scroll events]({% post_url 2022-04-13-detect-scroll-direction-vanilla-javascript %}#performance-considerations).
2. It uses the Intersection Observer API to show the button when the footer comes into view. This seems kind of pointless to me. Why even use JavaScript, just put the link in the footer and call it a day! I guess it is just to demo the code for using Intersection Observer, but it is not a very practical example in my opinion.