🔝

上へ戻るボタンを実装する

2023/02/02に公開

コピペでサッと使える上へ戻るボタンです。(2023年2月4日追記)

HTML

<body id="top">
〜〜〜
<a href="#top" id="toTop" class="c-toTop" aria-hidden="true">
  <span class="screen-reader-text">上へ戻る</span>
</a>

HTML はこちらだけです。
最初は非表示、スクロールしたら出現するようにしたいので、aria-hidden="true" を設定しています。
#top は設定しなくても仕様上ページトップへのリンクとなっているので、このまま使います。
https://blog.hog.as/entry/2023/01/05/000000
上記仕様について、Twitterでご指摘いただきました。[2023年2月4日追記 ここから]
https://twitter.com/hiro_ghap1/status/1621699611097440257?s=20&t=SVL8AdQjo2vhqJ-2C4_bsw
Chrome、Safari、Firefoxしか検証していませんが、適切にフォーカスの移動が行われたのは Chrome のみでしたので、body に id を明示するように変更しました。
えびちゃんさん、ご指摘ありがとうございました。[2023年2月4日追記 ここまで]

SCSS

html {
  scroll-behavior: smooth;
}

.c-toTop {
  position: fixed;
  right: 10px;
  bottom: 10px;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 50px;
  height: 50px;
  border: 1px solid #333333;
  border-radius: 50%;
  background-color: #fff;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: .3s background-color ease-out;
  &[aria-hidden="false"] {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    animation: .5s toTopFadeIn both;
  }
  &::before {
    content: '';
    display: inline-block;
    width: 15px;
    height: 8px;
    background-color: #333333;
    clip-path: polygon(0 70%, 50% 0, 100% 70%, 100% 100%, 50% 30%, 0 100%);
    transition: .3s transform ease-out;
  }
  &:hover {
    background-color: #f0f0f0;
    &::before {
      transform: translateY(-3px);
    }
  }
}
@keyframes toTopFadeIn {
  0% {
    visibility: hidden;
    opacity: 0;
  }
  1% {
    visibility: visible;
    opacity: 0;
  }
  100% {
    visibility: visible;
    opacity: 1;
  }
}

.screen-reader-text {
    clip: rect(1px, 1px, 1px, 1px);
    position: absolute !important;
    height: 1px;
    width: 1px;
    overflow: hidden;
}

htmlに scroll-behavior: smoothを設定して、スムーススクロールを実装します。
https://caniuse.com/?search=scroll-behavior
Safariの対応が15.4以上になるので、今のところ(2023年2月現在)は別途JavaScriptでスムーススクロールの実装が必要かなと思います。

大きさや位置は適宜調整してください。
初期状態をopacity: 0visibility: hiddenで不可視にしておき、aria-hidden="false"になったら可視状態にしています。
::beforeで上向き矢印を描いています。
screen-reader-textクラスで読み上げ用のspanタグの見た目を非表示にしています。

JavaScript

(new IntersectionObserver((entries) => {
    document.getElementById('toTop')
    .setAttribute('aria-hidden', entries[0].isIntersecting);
})).observe(document.querySelector('header'));

IntersectionObserver で要素を監視します。
ここではheaderを監視して、headerが viewport内にあれば#toToparia-hidden="true"に、viewport外のときはaria-hidden="false"にしています。

Discussion