JavaScriptおれおれAdvent Calendar 2014 – 21日目

よくある処理についてまとめました。

スクロールの監視

windowを対象にscrollイベントを監視するだけです。documentでも動くけど、古いIEは駄目。いちいち変える理由もないので、windowだけ使えばよろし。

$(window  ).on('scroll', function(event){ console.log('window'); });
$(document).on('scroll', function(event){ console.log('document'); });  // IE 8では呼ばれない

スクロール位置の取得

document.documentElement.scrollTopに現在のスクロール位置が格納されています。ちなみにdocument.documentElementはHTML要素の事。ただし、WebKitだけBODY要素から、document.body.scrollTopで得る必要があります。

一度だけ取得するならこんなコードで。

var scrollTop = document.documentElement.scrollTop || documentElement.body.scrollTop;

何度も取得するなら、要素を事前にキャッシュしておくと良いかと。

var elScrollable;
if (navigator.userAgent.indexOf('WebKit') < 0) {
    elScrollable = document.documentElement;
}
else {
    elScrollable = document.body;
}

var scrollTop = elScrollable.scrollTop;

ページ最下部へのスクロール到達判定

「スクロール位置 + 画面高さ」が「ページ高さ」と等しくなれば、最下部へ到達したものと考えます。

画面高さの取得

var windowHeight = document.documentElement.clientHeight;

ページ高さの取得

elScrollableは別項「スクロール位置の取得」で用意したもの。

var pageHeight = elScrollable.scrollHeight;

判定

var marginBottom = 0;
if (scrollTop + windowHeight + marginBottom >= pageHeight) {
    // 到達!
}

marginBottomは、完全に最下部へ到達する前に到達した事にする遊び(余白)です。

モバイル端末

iOSやAndroidでは、慣性スクロールまで全て終わってからイベントが発火します。

だので、追加読み込みみたいなのは最下部到達判定に遊びを用意して、ちょっと早めに読み込み開始するとUX高まると思う。(まあ利用者が途中で指を離してくれないといけないんだけど。)

iOS 7 Safariの「にょきっ」問題

iOS 7からSafariでヘッダー、フッターが「にょきっ」とするようになって、そこのあたりで画面の高さを取得するのがうまくいかない場合がある。画面が狭い方の状態が正であるようで、一度(フッターが隠れている状態で)最下部まで到達してから、もう一度下へ進めると、(フッターが出てきて)本来の最下部に到達できる。

もうちょっと言うと、最下部へは普通に到達できるんだけど、その際画面高さが高いままなので、理論上のスクロール位置まで到達しないんですよ。何これー。

特定要素までのスクロール到達判定

jQueryならoffset().topで取れるね。

// 要素の位置を取得
var targetTop = $elTarget.offset().top;

$(window).on('scroll', function(event){
    // elScrollableは別項「スクロール位置の取得」を参照
    var scrollTop = elScrollable.scrollTop;
    if (scrollTop >= targetTop) {
        // 要素到達時の処理
    }
});

画面最下部に要素が出てきたとき、なら上記で。画面中央とかまで待ちたいのなら、前述のwindowHeightを使って条件を調整してやる。

パフォーマンス問題

※本項の情報は古い可能性があります。(私自身の知識を更新しないと……。)

スクロールに合わせて~てのは、だいたい性能が問題になります。パララララックスって、だいたいかくかくするよね。

重い理由はだいたいこんな感じ。

  • scrollイベントは大量に発火する
  • 無駄な取得処理が多い
  • 強制画面更新を伴う値の乱用
  • 過度な描画更新

解説しようと思ったけど別稿に改めます。(早くもつかれた……。)

このあたりが詳しいと思う。

あれ、2011年か。まだ有効かな。あとこれ、日本語訳があった気がするんだけど……。

目次だけ翻訳しときます。参考になれば。

  • はじめに
  • スクロールとは何か
  • 描画診断
  • 画像のリサイズ
  • 高負荷なCSS
  • 再配置(reflow)と再描画(repaint)
  • scrollイベントの抑制
  • まとめ
  • あわせて読みたい

ライブラリー作った

せっかくなので。

こんな感じで使えます。前述の諸々の値、あれらの取得も楽ちんです。(README.mdのReferences参照。)

// scrollTop取得
$.scroller.on('scroll', function(event) {
    var scrollTop = $.scroller.top;
});

// 最下部到達
$.scroller.on('scrolltoend', function(event) {
    // したよ!
});

今後はjQuery依存を抜くのと、要素を指定してそこまでスクロールしたらコールバック実行、という機能を考えてます。