2019年8月1日

スクロール

スクロールイベントは、ページや要素のスクローリングに反応することができます。私たちがここでできることはたくさんあります。

例えば:

  • ユーザーがドキュメント内のどこにあるかに応じて、追加のコントロールや情報を表示/非表示する。
  • ユーザがページの下までスクロールしたときに追加のデータを読み込む。

ここに現在のスクロールを表示する小さい関数があります:

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

実行:

現在のスクロール = scroll the window

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

スクロールを防止する

どうやってスクロールをできなくするのでしょう? onscroll リスナの中で、event.preventDefault() によってスクロールを防止することはできません。なぜなら、それはスクロールがすでに行われた にトリガされるからです。

しかし、スクロールを引き起こすイベントの event.preventDefault()によるスクロールを防ぐことができます。

例えば:

  • wheel イベント – マウスホイールの回転 (“スクロール” タッチパッドアクションもそれを生成します)
  • pageUppageDown のための keydown

これは役立つ場合があります。しかし、他にもスクロールする方法があり、それらすべてを処理するのは非常に難しいです。そのため、何かをスクロールできないようにするには、 overflow プロパティのように CSS を使うのが、より信頼できる方法です。

ここでは、 onscroll 時のアプリケーションを解いたり、目を通すことができるタスクはほとんどありません。

タスク

重要性: 5

終わりのないページを作ってください。訪問者が最後までスクロールしたとき、現在の日付をテキストに自動追加します(訪問者がよりスクロールできるように)。

このようになります:

スクロールに関して2つの重要な特徴について留意してください:

  1. スクロールは “弾み” ます。 一部のブラウザ/デバイスでは、ドキュメントの開始または終了を超えてスクロールすることができます(下に空白が表示され、その後ドキュメントは自動的に通常に “戻されます”)。
  2. スクロールは精密ではありません。 ページを最後までスクロールするとき、実際には本当のドキュメントの底から 0-50px 程度離れている可能性があります。

したがって、“最後までスクロールする” とは、訪問者はドキュメントの底から 100px 以上は離れていないことを意味します。

P.S. 実践では、“より多くのメッセージ” や “より多くの商品” を表示したいです。

タスクのためのサンドボックスを開く

この解決策のコア部分は、ページの末尾にいるときに、日付情報をページに追加(もしくは、実践ではより多くのものを読み込む)する関数です。

私たちは、それをすぐに呼びだしたり、window.onscroll ハンドラとして追加することができます。

最も重要な問題は、“ページが末尾までスクロールされたかをどのようにして検出するか?” です。

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

ドキュメントは <html> タグ、つまり document.documentElement の中に表現されます。

document.documentElement.getBoundingClientRect() でドキュメント全体のウィンドウ相対座標を得ることができます。そして、bottom プロパティはドキュメントの終わりのウィンドウ相対座標です。

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

// ページのトップにいるとき
// ウィンドウ相対のトップは 0 です
document.documentElement.getBoundingClientRect().top = 0

// ウィンドウ相対の底 = 2000
// ドキュメントが長いので、おそらくウィンドウの底をはるかに超えています
document.documentElement.getBoundingClientRect().bottom = 2000

もし 500px 下にスクロールすると:

// ドキュメントトップはウィンドウより 500px 上にあります
document.documentElement.getBoundingClientRect().top = -500
// ドキュメントの底は 500px 近い
document.documentElement.getBoundingClientRect().bottom = 1500

最後までスクロールするとき、ウィンドウの高さを 600px と仮定すると:

document.documentElement.getBoundingClientRect().top = -1400
document.documentElement.getBoundingClientRect().bottom = 600

bottom は 0 にはなれないことに注意してください。なぜなら、決してウィンドウのトップには到達しないからです。底の座標の最も小さい制限はウィンドウの高さで、それ以上スクロールすることはできません。

また、ウィンドウの高さは document.documentElement.clientHeight です。

私たちは、ドキュメントの底部がそれからd 100px 以上は離れていてほしくないです。

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

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

    // もしウィンドウの高さ + 100px よりも大きい場合、ページの終わりではありません
    // (上の例の通り、大きい bottom はもっとスクロールが必要であることを意味します
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // それ以外はデータを追加する
    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 がないものもあります。 コードはそれらに触れるべきではありません。
  • 一旦ロードされた画像は、スクロールで画面内/外が変わってもそれ以上再読込されるべきではありません。

P.S. 可能であれば、現在の位置の1ページ前後にある画像を「プリロードする」より高度な解決策を作成してください。

P.P.S. 垂直スクロールのみが処理され、水平スクロールは処理されません。

タスクのためのサンドボックスを開く

onscroll ハンドラでどの画像が見えているのかを確認し、それらを表示する必要があります。

また、スクロールをする前にすぐに見える画像を検知してそれらを読み込むために、ページが読み込まれた時にもそれを実行したいです。

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

function isVisible(elem) {

  let coords = elem.getBoundingClientRect();

  let windowHeight = document.documentElement.clientHeight;

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

  return topVisible || bottomVisible;
}

showVisible();
window.onscroll = showVisible;

見えている画像に対して、img.dataset.src を取り、img.src に割り当てます(まだしていない場合)。

P.S. この解決策は1ページ上下にある画像を “プリロード” する isVisibe のバリアントも持っています(ページの高さは document.documentElement.clientHeight です)。

サンドボックスで解答を開く

チュートリアルマップ

コメント

コメントをする前に読んでください…
  • 自由に記事への追加や質問を投稿をしたり、それらに回答してください。
  • 数語のコードを挿入するには、<code> タグを使ってください。複数行の場合は <pre> を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。
  • 記事の中で理解できないことがあれば、詳しく説明してください。