2020年8月27日

スクロール

scroll イベントを使うと、ページや要素のスクロールに反応できます。これを使ってできることはたくさんあります。

例えば

  • ドキュメント内のユーザーの位置に応じて、追加のコントロールや情報を表示/非表示にする。
  • ユーザーがページの最後までスクロールすると、より多くのデータをロードする。

現在のスクロールを示す小さな関数を以下に示します。

window.addEventListener('scroll', function() {
  document.getElementById('showScroll').innerHTML = window.pageYOffset + 'px';
});

実行例

現在のスクロール = ウィンドウをスクロールしてください

scroll イベントは、window とスクロール可能な要素の両方で動作します。

スクロールを防止する

どのようにしてスクロールできないようにするのでしょうか?

onscroll リスナーで event.preventDefault() を使用してもスクロールを防ぐことはできません。なぜなら、これはスクロールがすでに発生したにトリガーされるからです。

しかし、スクロールを引き起こすイベント、たとえば pageUppageDownkeydown イベントで event.preventDefault() を使用することで、スクロールを防ぐことができます。

これらのイベントにイベントハンドラーを追加し、その中で event.preventDefault() を実行すると、スクロールは開始されません。

スクロールを開始する方法はたくさんあるため、CSSの overflow プロパティを使用する方が信頼性が高いです。

onscroll の応用例を確認するために、いくつかの課題を解決するか、見てみましょう。

課題

重要度: 5

無限ページを作成します。訪問者がページの最後までスクロールすると、現在の日時がテキストに自動的に追加されます(訪問者がさらにスクロールできるように)。

このように

スクロールの2つの重要な機能に注意してください

  1. スクロールは「伸縮性」があります。一部のブラウザ/デバイスでは、ドキュメントの先頭または末尾を少し超えてスクロールできます(下に空白が表示され、その後ドキュメントは自動的に「バウンスバック」して通常に戻ります)。
  2. スクロールは不正確です。ページの最後にスクロールすると、実際にはドキュメントの最下部から0〜50px離れている可能性があります。

したがって、「最後までスクロールする」とは、訪問者がドキュメントの最後から100px以内である必要があることを意味します。

PS。現実の世界では、「さらにメッセージ」や「さらに商品」を表示したい場合があります。

課題のサンドボックスを開きます。

解決策の中核は、ページの一番下にいるときにページに日付を追加する(または現実世界ではさらに多くのものをロードする)関数です。

すぐに呼び出して window.onscroll ハンドラーとして追加できます。

最も重要な質問は、「ページが一番下までスクロールしたことをどのように検出するのか」ということです。

ウィンドウ相対座標を使用しましょう。

ドキュメントは <html> タグ内に表現(および包含)されており、これは document.documentElement です。

ドキュメント全体のウィンドウ相対座標を document.documentElement.getBoundingClientRect() として取得できます。bottom プロパティは、ドキュメントの最下部のウィンドウ相対座標になります。

たとえば、HTMLドキュメント全体の高さが 2000px の場合

// when we're on the top of the page
// window-relative top = 0
document.documentElement.getBoundingClientRect().top = 0

// window-relative bottom = 2000
// the document is long, so that is probably far beyond the window bottom
document.documentElement.getBoundingClientRect().bottom = 2000

500px 下にスクロールした場合

// document top is above the window 500px
document.documentElement.getBoundingClientRect().top = -500
// document bottom is 500px closer
document.documentElement.getBoundingClientRect().bottom = 1500

ウィンドウの高さが 600px であると仮定して、最後までスクロールした場合

// document top is above the window 1400px
document.documentElement.getBoundingClientRect().top = -1400
// document bottom is below the window 600px
document.documentElement.getBoundingClientRect().bottom = 600

bottom はウィンドウの上部に到達しないため、0 になることはないことに注意してください。bottom 座標の最小限度はウィンドウの高さ(600 であると仮定)であり、これ以上上にスクロールすることはできません。

ウィンドウの高さは document.documentElement.clientHeight として取得できます。

この課題では、ドキュメントの一番下がそこから 100px 以内である(つまり、高さが 600 の場合は 600-700px)であるときを知る必要があります。

したがって、関数は次のようになります。

function populate() {
  while(true) {
    // document bottom
    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;

    // if the user hasn't scrolled far enough (>100px to the end)
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // let's add more data
    document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
  }
}

サンドボックスでソリューションを開きます。

重要度: 5

ページスクロールを支援する「トップへ」ボタンを作成します。

このように動作する必要があります。

  • ページがウィンドウの高さまでスクロールダウンしていない間は、非表示になります。
  • ページがウィンドウの高さ以上にスクロールダウンすると、左上の隅に「上向き」の矢印が表示されます。ページが元に戻ると、消えます。
  • 矢印をクリックすると、ページが一番上までスクロールします。

このように(左上隅、スクロールして確認)

課題のサンドボックスを開きます。

重要度: 4

クライアントの速度が遅く、モバイルトラフィックを節約したいとしましょう。

そのために、画像をすぐに表示するのではなく、次のようにプレースホルダーに置き換えることにしました。

<img src="placeholder.svg" width="128" height="128" data-src="real.jpg">

したがって、最初はすべての画像が placeholder.svg です。ページがユーザーが画像を見ることができる位置までスクロールすると、srcdata-src のものに変更して、画像がロードされます。

iframe の例を次に示します。

スクロールして、画像が「オンデマンド」でロードされるのを確認してください。

要件

  • ページがロードされると、画面に表示されている画像は、スクロールする前にすぐにロードする必要があります。
  • 一部の画像は、data-src なしで通常の場合があります。コードはそれらに触れないでください。
  • 画像がロードされたら、スクロールイン/アウト時に再度ロードしないでください。

PS。可能であれば、現在の位置から1ページ下/後にある画像を「プリロード」する、より高度なソリューションを作成してください。

PPS。垂直スクロールのみを処理し、水平スクロールは処理しないでください。

課題のサンドボックスを開きます。

onscroll ハンドラーは、どの画像が表示されているかを確認し、表示する必要があります。

ページがロードされたときにも、すぐに表示される画像を検出し、ロードするために実行したいと考えています。

コンテンツにアクセスできるように、ドキュメントがロードされたときにコードが実行されるようにする必要があります。

または、<body> の一番下に配置します。

// ...the page content is above...

function isVisible(elem) {

  let coords = elem.getBoundingClientRect();

  let windowHeight = document.documentElement.clientHeight;

  // top elem edge is visible?
  let topVisible = coords.top > 0 && coords.top < windowHeight;

  // bottom elem edge is visible?
  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;

  return topVisible || bottomVisible;
}

showVisible() 関数は、isVisible() で実装された可視性チェックを使用して、表示されている画像をロードします。

function showVisible() {
  for (let img of document.querySelectorAll('img')) {
    let realSrc = img.dataset.src;
    if (!realSrc) continue;

    if (isVisible(img)) {
      img.src = realSrc;
      img.dataset.src = '';
    }
  }
}

showVisible();
window.onscroll = showVisible;

PS。ソリューションには、現在のドキュメントスクロールから1ページ上下にある画像を「プリロード」する isVisible のバリアントもあります。

サンドボックスでソリューションを開きます。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善点に関する提案がある場合は、コメントする代わりに、GitHub issue を送信するか、プルリクエストを送信してください。
  • 記事の内容で理解できない場合は、詳しく説明してください。
  • 数語のコードを挿入するには、<code> タグを使用し、数行の場合は <pre> タグで囲み、10行以上の場合はサンドボックス(plnkr, jsbin, codepen…)を使用してください。