要素のスクロール時に処理を行う方法

スクロール位置の取得

element.onscroll = function()
{
    // スクロールされたピクセル数
    var scroll = this.scrollTop;

    // スクロール範囲の最大のピクセル数
    var range = this.scrollHeight - this.offsetHeight;
}
スクロールされた位置 0 px
スクロール範囲の最大値 1700 px
スクロールした割合 0 %
  1. 0 px
  2. 19 px
  3. 38 px
  4. 57 px
  5. 76 px
  6. 95 px
  7. 114 px
  8. 133 px
  9. 152 px
  10. 171 px
  11. 190 px
  12. 209 px
  13. 228 px
  14. 247 px
  15. 266 px
  16. 285 px
  17. 304 px
  18. 323 px
  19. 342 px
  20. 361 px
  21. 380 px
  22. 399 px
  23. 418 px
  24. 437 px
  25. 456 px
  26. 475 px
  27. 494 px
  28. 513 px
  29. 532 px
  30. 551 px
  31. 570 px
  32. 589 px
  33. 608 px
  34. 627 px
  35. 646 px
  36. 665 px
  37. 684 px
  38. 703 px
  39. 722 px
  40. 741 px
  41. 760 px
  42. 779 px
  43. 798 px
  44. 817 px
  45. 836 px
  46. 855 px
  47. 874 px
  48. 893 px
  49. 912 px
  50. 931 px
  51. 950 px
  52. 969 px
  53. 988 px
  54. 1007 px
  55. 1026 px
  56. 1045 px
  57. 1064 px
  58. 1083 px
  59. 1102 px
  60. 1121 px
  61. 1140 px
  62. 1159 px
  63. 1178 px
  64. 1197 px
  65. 1216 px
  66. 1235 px
  67. 1254 px
  68. 1273 px
  69. 1292 px
  70. 1311 px
  71. 1330 px
  72. 1349 px
  73. 1368 px
  74. 1387 px
  75. 1406 px
  76. 1425 px
  77. 1444 px
  78. 1463 px
  79. 1482 px
  80. 1501 px
  81. 1520 px
  82. 1539 px
  83. 1558 px
  84. 1577 px
  85. 1596 px
  86. 1615 px
  87. 1634 px
  88. 1653 px
  89. 1672 px
  90. 1691 px
  91. 1710 px
  92. 1729 px
  93. 1748 px
  94. 1767 px
  95. 1786 px
  96. 1805 px
  97. 1824 px
  98. 1843 px
  99. 1862 px
  100. 1881 px
※リストの数値は、親要素からのオフセット

ドキュメントのスクロール位置

ドキュメントがスクロールされた位置 (scrollTop) を取得するとき、ブラウザによってそれを返すオブジェクトが異なります。よって次のように、2つのオブジェクトから取得するようにします。

window.onscroll = function()
{
    var scrollTop =
        document.documentElement.scrollTop || // IE、Firefox、Opera
        document.body.scrollTop;              // Chrome、Safari
}

ただしこれらのオブジェクトは、

  • document.documentElement … html要素 (Document.documentElement)
  • document.body … body要素 (HTMLDocument.body)

のように異なる要素を参照するため、異なる値を返すことがあります。この違いはドキュメントのサイズとスクロール位置を確認するページで確認できます。

ところでこのscrollTopに値を設定すればドキュメントをスクロールできますが、Windowオブジェクトにはそれ専用のメソッドが用意されています。

スクロールに応じた処理

下端までスクロールされたら、末尾に要素を追加

表示領域の下端までスクロールされたときに、末尾に要素を追加します。初期状態では最小限の表示だけを行い、必要になった時点で要素を追加することで、パフォーマンスの向上を見込めます。

<div id="box" style="height: 100px; overflow: auto"></div>

<script type="text/javascript">
  var box = document.getElementById( 'box' );
  var lastElement = box.appendChild( document.createElement( 'div' ) );

  box.onscroll = function()
  {
      // 表示領域の下端の位置
      var bottom = this.scrollTop + this.clientHeight;

      // 末尾の要素の上端の位置
      var top = lastElement.offsetTop - this.offsetTop;

      if( top < bottom )
      {
          var div = document.createElement( 'div' );
          this.appendChild( div );

          // 追加した要素を最後の要素とする
          lastElement = div;
      }
  }
</script>
表示領域の下端の位置 200 px
末尾の要素の上端の位置 171 px
  1. 0 px
  2. 19 px
  3. 38 px
  4. 57 px
  5. 76 px
  6. 95 px
  7. 114 px
  8. 133 px
  9. 152 px
  10. 171 px
  11. 190 px
※リストの数値は、親要素からのオフセット

要素が見える範囲に入ったら、その要素の処理を実行 (遅延読み込み)

スクロールによって要素が可視範囲に入ってから、その要素の処理を実行します。これは処理に時間のかかる要素、たとえばファイルサイズの大きな画像などの読み込みを遅延させることで、ページ全体のパフォーマンスを向上させるような用途に有効です。

<div id="box" style="height: 100px; overflow: auto"></div>

<script type="text/javascript">
    function DelayLoad( box, elem, callback )
    {
        var CheckVisibility = function()
        {
            // 表示領域の下端の位置
            var bottom = box.scrollTop + box.clientHeight;

            // 要素の上端の位置
            var top = elem.offsetTop - box.offsetTop;

            if( top < bottom )
            {
                // イベントハンドラを削除する
                if( box.removeEventListener )
                {
                    box.removeEventListener( 'scroll', CheckVisibility, false );
                }
                else if( box.detachEvent )
                {
                    box.detachEvent( 'onscroll', CheckVisibility );
                }

                // コールバックを呼び出す
                callback( elem );
            }
        }

        // scrollに応答して要素の状態を調べるように、ハンドラを登録する
        // Internet Explorer 9より前をサポートしないならば、addEventListenerだけで十分
        if( box.addEventListener )
        {
            box.addEventListener( 'scroll', CheckVisibility, false );

            // ドキュメントの構築を待ってから初期化する
            window.setTimeout( CheckVisibility, 0 );
        }
        else if( box.attachEvent )
        {
            box.attachEvent( 'onscroll', CheckVisibility );
            window.setTimeout( CheckVisibility, 0 );
        }
        else
        {
            callback( elem );
        }
    }

    //
    var box = document.getElementById( 'box' );

    var list = document.createElement( 'ol' );
    box.appendChild( list );

    for( var i = 0; i < 100; i++ )
    {
        var li = document.createElement( 'li' );
        list.appendChild( li );

        // 各要素の状態監視と、コールバックの登録
        DelayLoad( box, li, function( elem )
            {
                // 1000ミリ秒後に、渡された要素内に'ok'と表示
                window.setTimeout( function() { elem.innerHTML = 'ok'; }, 1000 );
            } );
    }
</script>

可視範囲に入った後、1秒後に[ok]と表示します。

遅延読み込みを実現する方法として、jQueryのプラグインであるLazy Loadを使用する方法もあります。

ドキュメント内でスクロールする場合

スクロール範囲が要素内ではなくドキュメント内であるならば、ウィンドウの表示領域の大きさを基準とします。

var targetElement; // 対象とする要素

window.onscroll = function()
{
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

    // ウィンドウのビューポートの下端の位置
    var bottom = scrollTop + document.documentElement.clientHeight;

    // 対象とする要素の上端の位置
    var top = targetElement.offsetTop;

    if( top < bottom )
    {
        // 可視範囲に入ったときの処理
    }
}
JavaScriptのドキュメントから検索