ピュアCSSスムーススクロール"戻る"トップへ


This is the fourth post in a series examining modern CSS solutions to problems I've been solving over the last 13+ years of being a frontend developer. Visit ModernCSS.dev to view the whole series and additional resources.


“戻るto top”リンクは、これらの日に頻繁に使用されていない可能性がありますが、テクニックがよく示している2つの近代的なCSSの機能があります
  • position: sticky
  • scroll-behavior: smooth
  • 私は最初に「トップへ戻る」リンクに恋をしました-そして、次に、jQueryでそれをする方法を学びました- 2011年の最もゴージャスなサイトのうちの1つでWeb Designer Wall .
    アイデアは、ウェブサイトの先頭にスクロールして、しばしばYOREのブログで使用された“ジャンプリンク”をユーザに提供することです.
    我々が達成するために学ぶものは、ここにあります:

    位置について:粘着性


    この新しい位置の値は以下の通りですcaniuse :

    Keeps elements positioned as "fixed" or "relative" depending on how it appears in the viewport. As a result, the element is "stuck" when necessary while scrolling.


    Caniuseデータからの他の重要な注意点は、それが最高のサポートのためにプレフィックスを提供する必要があることです.私たちのデモはposition: fixed これはわずかに少ない優雅な主な目標を達成します.

    スクロール動作について


    これは非常に新しいプロパティですsupport is relatively low . この正確な定義は、特にアンカーリンクの選択時にスクロール動作を要求します.
    それを使用して“進歩的な強化”は、ブラウザがサポートしている人のためのより良い経験になることを意味するが、それはないブラウザのために動作します.
    驚いたことに、サファリはこれを支持することにあります、しかし、他の主要なブラウザーはします.

    ステージを設定する


    まず、基本的なコンテンツ形式のセマンティックマークアップを設定する必要があります.
    <header id="top">Title</header>
    <main>
      <article>
        <!-- long form content here -->
      </article>
      <!-- Back to Top link -->
      <div class="back-to-top-wrapper">
        <a href="#top" class="back-to-top-link" aria-label="Scroll to Top">🔝</a>
      </div>
    </main>
    
    我々は、後に我々のリンクを置きますarticle , 内からmain . それは特に記事の一部でありません、そして、我々はそれが焦点オーダーで最後であることを望みます.
    また、id="top"<header> そしてそのアンカーをhref 先頭リンクへの値.あなただけの上部にスクロールしたい場合<main> IDを移動したり、ページの上部にある既存のIDに付けたりできます.

    滑らかなスクロールを追加


    私たちの目的の最初の部分は簡単なpeasyです、そして、以下のCSS規則によって達成されます:
    /* Smooth scrolling IF user doesn't have a preference due to motion sensitivities */
    @media screen and (prefers-reduced-motion: no-preference) {
      html {
        scroll-behavior: smooth;
      }
    }
    
    Hトゥティー
    オリジナルの解決策が説明できなかったことを指摘するprefers-reduced-motion以前、私はこのCSS-Tricks article jQueryとバニラJSでこれを達成するためにブックマークしました.記事はしばらくの間、されており、継続的にそのような新しい方法が利用可能な記事を更新するためにそのチームに👍
    私はいくつかの奇妙な発見をしたときなどのページをご覧ください#anchor URLでは、まだシナリオのために実際に望ましいかもしれないスムーズなスクロールを実行します.

    スタイルは、トップへ戻る


    作業する前に、基本的なスタイルをリンクに適用しましょう.楽しみのために、私は絵文字を使用したが、スタイルのより多くの制御のためのSVGのアイコンを交換することができます.
    .back-to-top-link {
      display: inline-block;
      text-decoration: none;
      font-size: 2rem;
      line-height: 3rem;
      text-align: center;
      width: 3rem;
      height: 3rem;
      border-radius: 50%;
      background-color: #D6E3F0;
      /* emoji don't behave like regular fonts
         so this helped position it correctly */
      padding: 0.25rem;
    }
    
    これは非常に基本的なラウンドボタンを与えます.完全なcodepenでは、私は追加の:hover and :focus スタイリングが、それらは不可欠ではありません.
    次に、なぜこのリンクのラッパーを追加したのか不思議に思うかもしれません.その理由は、基本的には「リンクトラック」を作成して、その中に住むためのリンクを作る必要があるということです.
    position: sticky 設計されて、それはDOMに置かれている場所から要素を拾う.そして、ビューポートに対して“top”値を付けます.しかし、我々はドキュメントの終わりにリンクを置いたので、それは基本的に決して援助なしで拾われません.
    我々は、ラッパーを使用しますposition: absolute リンクの位置をページ上で視覚的に高くするように変更します.ありがたいことに、ブラウザは、この視覚的に調整された位置を使用します-それがViewPortに入るとき、別名「リンク」を計算するために.
    したがって、ラッパースタイルは次のようになります.
    /* How far of a scroll travel within <main> prior to the
       link appearing */
    $scrollLength: 100vh;
    
    .back-to-top-wrapper {
      // uncomment to visualize "track"
      // outline: 1px solid red;
      position: absolute;
      top: $scrollLength;
      right: 0.25rem;
      // Optional, extends the final link into the 
      // footer at the bottom of the page
      // Set to `0` to stop at the end of `main`
      bottom: -5em;
      // Required for best support in browsers not supporting `sticky`
      width: 3em;
      // Disable interaction with this element
      pointer-events: none;
    }
    
    最後の定義はあなたにとって新しいかもしれません.pointer-events: none . これは本質的にクリックのような相互作用イベントを「要素を通り抜けます」、それがドキュメントの残りをブロックしないようにします.例えば、このラッパーを誤ってコンテンツ内のリンクをブロックしたくないでしょう.
    これにより、現在のビューポートのコンテンツの少し下にあるコンテンツをオーバーラップするリンクが表示されます.スタイリングを加えましょう<main> このオーバーラップを防止し、position: relative 絶対リンクラッパーを指定した場合に最適な結果が必要です.
    main {
      // leave room for the "scroll track"
      padding: 0 3rem;
      // required to make sure the `absolute` positioning of
      // the anchor wrapper is indeed `relative` to this element vs. the body
      position: relative;
      max-width: 50rem;
      margin: 2rem auto;
    
      // Optional, clears margin if last element is a block item 
      *:last-child {
        margin-bottom: 0;
      }
    }
    
    残っているすべては、完全な仕事に位置決めのために必要なスタイルを加えるために関連を再訪することです:
    .back-to-top-link {
      // `fixed` is fallback when `sticky` not supported
      position: fixed;
      // preferred positioning, requires prefixing for most support, and not supported on Safari
      // @link https://caniuse.com/#search=position%3A%20sticky
      position: sticky;
      // reinstate clicks
      pointer-events: all;
      // achieves desired positioning within the viewport
      // relative to the top of the viewport once `sticky` takes over, or always if `fixed` fallback is used
      top: calc(100vh - 5rem);
    
      // ... styles written earlier
    }
    
    The fixed フォールバックは、サポートしていないブラウザを意味しますsticky ポジショニングは常にリンクを表示します.時sticky がサポートされている場合、完全に望ましい効果が発生します$scrollLength が渡される.With sticky 位置、一度ラッパーの上部がビューポートにある場合、リンクは“スタック”であり、ユーザーとスクロールします.
    我々がまた復帰する通知pointer-eventsall リンクとの相互作用が実際に働くような値.
    そして、ここで最終的な結果は-ビューのベストサファリのための非サファリです.

    既知の問題


    短いフォームのコンテンツがあれば、これはより短いデバイスビューポートで非常にうまく機能しません.あなたは考えるかもしれませんoverflow: hidden . しかし、残念ながら、防止position: sticky 完全に働くことから☹️ それで、「現実世界」シナリオでは、あなたは記事につきこの振舞いをトグルするオプションを提供しなければならないかもしれません、あるいは、記事がテンプレートでそれを注射する前に記事が長さ要件に会うかどうか決定するために計算を実行しなければなりません.
    あなたが知っているか、これらのメソッドで他の「gotchas」を見つけるならば、コメントを落としてください!