jQuery.stopのjumpToEnd引数が便利すぎてやばい(JavaScript Advent Calendar 2010 5日目)

JavaScript Advent Calendar 20105日目のago@kyo_ago)です。

jQueryのソースを眺めててjQuery.stopに引数があることに気づいたので調べてみました。

まず、簡単なスライドダウンメニューを作ってみたいと思います。

jQuery.stop 1 - jsdo.it - share JavaScript, HTML5 and CSS

JS部分は以下の通りです。

$(function () {
    $('div').hover(function () {
        $(this).find('ul').slideDown();
    }, function () {
        $(this).find('ul').slideUp();
    });
});

少し触ると分かると思いますが、マウスがmenuから外れた後も何度も.slideDown、.slideUpが実行されるため非常に操作性が悪いです。

jQueryはアニメーションが複数同時に呼ばれた場合、先に呼ばれたアニメーションが終了するまで次のアニメーションが呼び出されないように実装されており、このためユーザの操作で呼び出されるアニメーションをそのまま実装するとこういった動作になってしまいます。

こういった問題を解決するためにjQueryには.stopというメソッドが用意されており、このメソッドを呼び出すとその時点で実行されているアニメーションを停止させることが出来ます。

jQuery.stop 2 - jsdo.it - share JavaScript, HTML5 and CSS

JS部分は以下の通りです。

$(function () {
    $('div').hover(function () {
        $(this).find('ul').stop().slideDown();
    }, function () {
        $(this).find('ul').stop().slideUp();
    });
});

.stop無しのものにあった「マウスがmenuから外れた後も何度も.slideDown、.slideUpが実行される」問題は解決しています。

ただこのコードでも速い速度でmenu1?menu3のあいだを何度もマウスが往復しているとだんだんsub1?sub3の高さが減っていくと思います。

これは.stopが単純にアニメーションを停止させるだけなので、.slideDownの途中で呼び出された場合、その時点のheightが次回以降のheightとして認識されることが原因です。
(「.slideDownの途中で.stop」->「.stopが呼び出された時点のheightがその要素のheightとして設定されたままになる」->「次の.slideDownも前回のheightまでしか高さが変化しない」)

jQueryはこれに関しても対応が用意されており、.stopの第2引数(jumpToEnd)にtrueを渡してやると、アニメーションを停止した上で停止されたアニメーションの終了時点の表示まで処理を飛ばしてくれます。

jQuery.stop 3 - jsdo.it - share JavaScript, HTML5 and CSS

JS部分は以下の通りです。

$(function () {
    $('div').hover(function () {
        $(this).find('ul').stop(true, true).slideDown();
    }, function () {
        $(this).find('ul').stop(true, true).slideUp();
    });
});

どうでしょうか?最初のコードに対して.stop(true, true)を追加しただけですが、これだけで普通に使えるスライドダウンメニューになったのではないかと思います。

ちなみに第1引数(clearQueue)はその時点で実行待ちになっているアニメーションを全て停止させる指定ですが念のためtrueを指定しています。
(こういったユーザ動作で発生するアニメーションの場合、同時に実行されることは無いと思います)

個人的には非常に便利だと思うんですが、現状日本語のリファレンスサイトには解説されていないため、ドキュメントを確認したい場合本家APIの.stopを確認してください。

カヤックではAdvent Calendar好きな技術者も募集しています!