本文へスキップ

Topへ戻る

スクロールすると右下に「↑ Top」ボタンが浮かび上がり、押すと先頭へ滑らかに戻る。

プレビュー

スクロール

操作枠内をスクロールすると動きます。

使いどころ

長い記事や一覧ページのように、下までスクロールしてから最初に戻りたくなる場面に向く。逆に、1〜2画面で収まる短いページでは出す意味が薄く、常時見えてしまうと『何のためのボタンか』が分かりにくくなる。表示のしきい値(このデモでは80px)はコンテンツの分量に応じて調整すべきで、ファーストビューを出た瞬間に出すと『すぐ現れて邪魔』に感じやすい。モバイルの下部にタブバーやチャットウィジェットなど他の固定要素がある場合は、位置が重ならないよう配置を調整するか、その画面ではこのボタン自体を出さない判断も要る。

仕組み

表示・非表示は display の切り替えではなく opacity と transform(translateY) を transition させる二状態方式で行っている。display: none にすると transition が発火しないため、この実装は常にDOM上に存在させたまま opacity: 0 + pointer-events: none で『見えないが位置は確保されている』状態を作り、.is-visible クラスが付くと opacity: 1 + pointer-events: auto に切り替わる。表示判定は scroll イベントを passive で監視し、scrollTop が80pxを超えたかどうかだけで is-visible をトグルする単純なロジック。クリック時のスクロールは JS でアニメーションループを書かず、documentElement.scrollTo({ top: 0, behavior: 'smooth' }) というブラウザネイティブのスムーズスクロールに任せている。

実装の注意

非表示時に pointer-events: none を付け忘れると、opacity: 0 で見えないだけのボタンが position: fixed のまま画面右下に居座り続け、その真下にある本来クリックしたい要素(リンクやボタン)の当たり判定を横取りしてしまう。この実装はそこを塞いでいるが、コピーして改造するときに見落としやすい点。scroll イベントに { passive: true } を付けていないと、スクロール中に毎回ブラウザがpreventDefaultの有無を確認する必要が生じ、スクロールがカクつく原因になる。また position: fixed の bottom: 20px は、モバイルでアドレスバーが伸縮するブラウザやホームバー(セーフエリア)のある端末では下端に近づきすぎることがあり、bottom: max(20px, env(safe-area-inset-bottom)) のような調整が実務では必要になる。

アクセシビリティ

ボタン要素自体はキーボードでフォーカス可能だが、この実装は非表示時に aria-hidden や tabindex="-1" を付けていないため、opacity: 0 で見えない状態でもTabキーでフォーカスが当たってしまう。キーボード・スクリーンリーダー利用者からは『見えない謎のボタン』としてタブ移動の途中に現れることになるので、.is-visible の切り替えに合わせて tabindex="-1" の付け外し(あるいは disabled / inert / display:none での非表示化)を行うのが必須の修正で、aria-hidden の同期はそれだけではタブ順から外れない——読み上げツリー用の補助として併用する位置づけになる。加えて、クリック後にスクロールが先頭へ戻ってもフォーカスはボタンに残ったままなので、ページ本文の見出しなど適切な要素へフォーカスを移すと、スクリーンリーダー利用者にも『先頭に戻った』ことが伝わりやすくなる。prefers-reduced-motion: reduce では scrollTo の behavior を 'smooth' から 'auto' に切り替えて瞬間移動にし、表示アニメーションの transition も短縮・削除すべき。

調整して使う

しきい値(スクロール量)・位置(右下/左下)・アイコンかテキストか(↑のみ/『Top』併記)が主な調整点。読了までの残量を伝えたいなら、円形のプログレスリングをボタンの周囲に描き足して現在位置を視覚化する応用もできる。フッターに専用の『トップへ戻る』リンクがすでにあるページでは、フローティングボタンは冗長になるので、フッター手前で非表示に戻す(スクロール位置が下端付近ならis-visibleを外す)判断もできる。表示のしきい値は固定px(現在80px)ではなく、ページ全体の高さに対する割合(例えば20%スクロールしたら)にしておくと、極端に長い/短いページが混在するサイトでも体感がそろう。

コード(コピーして使えます)

<div class="btt-hint">↓ スクロール ↓</div>
<div class="btt-filler">
  <p>もっと下へスクロールすると<br>右下にボタンが現れます</p>
</div>
<div class="btt-filler btt-filler--end">
  <p>ここまで来たら<br>ボタンを押してみて</p>
</div>
<button class="btt-btn" aria-label="先頭へ戻る">↑ Top</button>

タグ

同じカテゴリ「ナビ・リスト操作」のほかのパーツ

関連パーツ