jQuery でページスクロール

jQuery 1.2 以降だと scrollTop(と scrollLeft)という疑似スタイルが利用できるようになったので,jQuery UI を使わずとも,jQuery Core 本体だけでページ内スクロールができるようになりました。

たとえば,

<html>
    <body>
        <p>
            blah, blah, blah, ...
            blah, blah, blah, ...
        </p>

        <p>
            <a href="#" id="link_to_top">ページの先頭へ</a>
        </p>
    </body>
</html>

このような HTML で「ページの先頭へ」というリンクをクリックしたときに,スムーズにスクロールしたいのなら,

$(function () {
    if (! $.browser.safari) {
        $('#link_to_top').click(function () {
            $(this).blur();

            $('html,body').animate({ scrollTop: 0 }, 'slow');

            return false;
        });
    }
});

のように記述するだけで OK です*1

トップに移動したときにロケーションバーの URL が # 付きになるのがイヤ & デフォルト動作を回避するために return false; で帰ってます。

jQueryscrollTop 疑似スタイルとは?

DOM における scrollTop プロパティのことではありません。

jQuery Core の 汎用アニメーション関数の [http://docs.jquery.com/Effects/animate:title=animate()] って,アニメーションのターゲットとして指定できるのがスタイルだけなんです。

で,たとえばブロックのスライドダウンなどの場合,スタイルの height をいじればすみます。しかしスクロールのポジションについてはスタイル側にプロパティが用意されていません(DOM 側の scrollTop プロパティで操作する)。それだと不便だよね,ということで,jQuery 1.2 では css 側の関数として scrollTop() などを用意したそうです。詳しくはMilestone 1.2 – jQuery - Bug Tracker を参照してください。

ちなみに DOM インタフェース経由でスタイルを直接いじっても反映されるというわけではないです。あくまで jQuery[http://docs.jquery.com/CSS/css#namevalue:title=css()][http://docs.jquery.com/Effects/animate:title=animate()] などの関数経由で指定できるようになったということです。また直接値を取得・設定する [http://docs.jquery.com/CSS/scrollTop:title=scrollTop()][http://docs.jquery.com/CSS/scrollTop:title=scrollLeft()] といった関数もあります。

なぜ $('html,body') と指定しているのか

最初 jQuery ドキュメントの例 の通り,$('body') とだけ書いていてうまくいかないよーと悩んでいました。

んで,検索したら下記のページがひっかかりました。

But why do we need to select both body and html? Well, Firefox and IE use body in quirks mode but html in standards mode. Our $('html, body') selector takes both situations into account. Of course, if you know your pages are running in standards mode (which they should), then you can drop the body (and the comma) from the selector.

Animated Scrolling with jQuery 1.2 | Learning jQuery

ようするに,Firefox と IE では,後方互換性モードの場合は <body>scrollTop を指定するべきであるのに対して,標準準拠モードの場合は <html> のそれを指定するべきだということだそうです。なので,念のために html,body と両方指定すればいずれのモードでも動くようにできます。

イージングしたい

上記のコードだと,リンクをクリックするとぬるぬるとスクロールしていきます。これを改善するのがイージングです。

イージングを使うと,たとえば初速はそれなりの速度で着地するにはふわっとさせたり,逆に初速はゆるゆると動くけれどだんだんスピードがあがったり,などさせることができます。つまりアニメーション速度を関数で制御できるようにする機構のことです。

jQuery Core には linearswing という2つのイージング関数が登録されているのですが,jQuery Easing Plugin を使えばもっといろんなイージングができます。

このプラグイン全体を読み込んでもいいのですが,実際に利用するのは1つか2つくらいのものでしょう。その場合,ソースから必要な関数だけコピペすれば充分です。

たとえば,

	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},

このイージング関数を利用したい場合,以下のようにコピペすれば*2最小限のソースで利用することができます。

jQuery.easing.quart = function (x, t, b, c, d) {
    return -c * ((t=t/d-1)*t*t*t - 1) + b;
};  

$(function () {
    $('#link_to_top').click(function () {
        $('html,body').animate({ scrollTop: 0 }, 300, 'quart');
    });
});

応用

今回の例だとページトップへのリンクのみスムーススクロールするようにしましたけれど,他のアンカーへ飛ぶときにも利用するのもオツでしょう。

先ほど引用した Animated Scrolling with jQuery 1.2 | Learning jQuery からの例ですが,

var targetOffset = $('#hogehoge').offset().top;
$('html,body').animate({scrollTop: targetOffset}, 1000);

のようにターゲットの絶対位置を offset() 関数で取得して,0 の代わりにそれを scrollTop に指定すれば,ターゲットまでスクロールさせることも可能です。


さらに,このページには $('a[href*=#]') で全アンカーへのリンクを取得して自動的にスクロールするように適用するコードも載っています。また,html(や body)だけでなく,overflowscroll な要素に対して適用する例も載っています。

*1:Safari ではうまくアニメーションしないそうなので,ブラウザ避けをはさんであります。でもよくよく考えたらアニメーションしないだけでズバっと移動はしてくれるらしいのでブラウザ避けするまでもなかったかも。

*2:jQuery Easing Plugin は BSD License なので,実地で使うにはソース冒頭のライセンス表記もコピーしたほうがよいです。