- 「操作していないのに後から動く」の正体
- どうしてキューが溜まるのか
- 実際に起きる挙動
- さらに深刻なケース
- パフォーマンスへの影響
- `stop()`だけでは足りない理由
- `finish()`との違い
- キューが詰まりやすいUI
- 根本対策
- CSSアニメーションとの違い
- 最後に
「操作していないのに後から動く」の正体
jQueryのUIでよくある不可解な現象があります。
- クリックしていないのにメニューが動く
- 数秒後にアニメーションが始まる
- スクロール後に突然開閉する
- 連打すると壊れたように見える
多くの場合、原因はバグではありません。
アニメーションキューが詰まっている状態です。
jQueryはアニメーションを即時実行せず、待ち行列(queue)に積んで順番に処理します。
その待ち行列が大量に溜まると、ユーザー操作と関係ないタイミングで動作が始まります。
どうしてキューが溜まるのか
典型的なコードです。
$("#button").click(function(){ $("#panel").slideToggle(300); });
一見問題ありません。
しかしユーザーは1回だけクリックするとは限りません。
- ダブルクリック
- 連打
- タップ連続
そのたびに`slideToggle()`が呼ばれます。
呼ばれるたびに、jQueryは次を行います。
1 アニメーション関数生成
2 fxキューに追加
3 現在のアニメーション終了を待機
つまり、操作回数分だけ「予約」が積まれます。
実際に起きる挙動
例えば5回連続クリックした場合:
| 順番 | キュー内容 |
| 1 | 開く |
| 2 | 閉じる |
| 3 | 開く |
| 4 | 閉じる |
| 5 | 開く |
ユーザーはすでに操作をやめていますが、
キューは順番に実行され続けます。
これが「勝手に動く」現象の正体です。
さらに深刻なケース
ホバーイベントで起きやすいです。
$(".menu").hover( function(){ $("#panel").slideDown(); }, function(){ $("#panel").slideUp(); } );
マウスを高速に出入りすると、
開閉アニメーションが何十個もキューに積まれます。
結果:
- 開きっぱなし
- 閉じない
- ずっと動き続ける
これは描画バグではなく、キューの処理待ちです。
パフォーマンスへの影響
キューが増えると単に見た目がおかしくなるだけではありません。
内部では次が起きます。
- タイマー処理増加
- DOM書き換え予約増加
- レイアウト計算増加
つまりCPU負荷が上がります。
スマートフォンではスクロール不能になることもあります。
特に怖いのは、
ユーザーが操作をやめても負荷が続く点です。
`stop()`だけでは足りない理由
よくある対策です。
$("#panel").stop().slideToggle();
しかしこれではキューは残ります。
現在のアニメーションを止めるだけです。
必要なのはこれです。
$("#panel").stop(true,true).slideToggle();
意味:
- 第一引数:待機キューを削除
- 第二引数:現在状態を終了位置へ
これで予約された動作が消えます。
`finish()`との違い
さらに強制的な方法があります。
$("#panel").finish();
`finish()`は
- 現在アニメーションを即時完了
- キューを全実行状態に
つまり途中状態を残さず最終状態にします。
UIをリセットしたいときに使われます。
キューが詰まりやすいUI
実務で頻発する箇所です。
- ハンバーガーメニュー
- アコーディオン
- ドロップダウンメニュー
- モーダル表示
- 無限スクロール読み込み表示
「ユーザー操作とアニメーションが同時に起きるUI」は特に危険です。
根本対策
最も確実なのは「状態を持つ」ことです。
let opened = false; $("#button").click(function(){ if(opened) return; opened = true; $("#panel").slideDown(300, function(){ opened = false; }); });
アニメーション中の再実行を防ぎます。
キュー自体を増やさない方法です。
CSSアニメーションとの違い
CSS transitionではキューがありません。
新しい命令は現在状態を上書きします。
つまり
- jQuery:予約型
- CSS:状態型
この違いが、近年CSSアニメーションが推奨される理由の1つです。
最後に
アニメーションキューが詰まると、
バグのように見える挙動のほとんどが説明できます。
jQueryは「命令を順番に守る」設計です。
しかしUIは「現在の状態に反応する」ものです。
このズレが問題を生みます。
対策は単純で、アニメーションを命令として扱うのではなく、
状態遷移として設計することです。
それだけで、多くの不可解な挙動は消えます。