jQueryの slideToggle() が「ガタつく」「最初にジャンプする」という問題は、margin・padding の扱いやHTML構造が原因であることがほとんどです。この記事ではカクつきの原因別の対処法・CSSトランジションへの移行・スムーズなアコーディオン実装まで解説します。
この記事でわかること
- slideToggle()がカクつく主な原因(padding/margin問題)
- 原因別の対処法
- CSSトランジションでスムーズに切り替える方法(推奨)
- スムーズなアコーディオンの実装パターン
- display:none の要素を slideDown で開くときの注意点
カクつく原因のまとめ
| 原因 | 症状 | 対処法 |
|---|---|---|
| 要素にpaddingがある | 開く時に最初にジャンプ | ラッパー要素でpadding/marginを管理 |
| 子要素のmarginがある | 最後に突然位置が変わる | ラッパーでラップするかmarginをpaddingに変更 |
| display:noneの初期高さ問題 | 最初の1回だけカクつく | CSSでdisplay:noneではなくmax-heightで制御 |
| フォント読み込み中 | フォント適用時に高さが変化 | font-displayや先読みで対処 |
原因1: paddingによるジャンプの対処法
slideToggle対象の要素に直接paddingを設定すると、スライド開始時にpaddingが瞬時に適用されてジャンプします。解決策はpadding管理を内側のラッパー要素に移すことです。
<!-- NG: 直接paddingを設定 --> <div class="panel" style="display:none; padding: 16px;">コンテンツ</div> <!-- OK: 外側は padding なし、内側でpadding管理 --> <div class="panel" style="display:none;"> <div class="panel-inner" style="padding: 16px;">コンテンツ</div> </div>
// 外側の .panel に対して slideToggle
$('.toggle-btn').on('click', function () {
$('.panel').slideToggle(300);
});
原因2: 子要素のmarginによるガタつきの対処法
スライド領域内の子要素に margin-top や margin-bottom があると、コンテナのオーバーフロー設定によってはガタつきが生じます。
/* NG: 子要素に margin がある */
.panel p {
margin-top: 16px;
margin-bottom: 16px;
}
/* OK: margin の代わりに padding を使う */
.panel p {
padding-top: 8px;
padding-bottom: 8px;
margin: 0;
}
/* または overflow: hidden で margin を封じ込める */
.panel {
overflow: hidden;
}
CSSトランジションでスムーズに切り替える(推奨)
jQueryの slideToggle() よりも、CSSの max-height トランジションの方がGPU加速で滑らかに動作します。
.panel {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease;
}
.panel.is-open {
max-height: 500px; /* コンテンツの最大高さより大きい値に設定 */
}
$(function () {
$('.toggle-btn').on('click', function () {
// クラスを付け外しするだけ(CSSがアニメーションを担当)
$(this).next('.panel').toggleClass('is-open');
});
});
max-heightの値は実際のコンテンツ高さより大きく設定する
max-height: 0 から max-height: 500px にアニメーションする際、実際のコンテンツ高さが100pxなら閉じる速度が500pxから0への速度になるため少し速く感じます。コンテンツ高さに近い値(余裕を持たせて)に設定するか、JavaScriptで実際の高さを取得してセットする方法もあります。スムーズなアコーディオンの実装
<div class="accordion">
<div class="accordion-item">
<button class="accordion-header">セクション1</button>
<div class="accordion-body"><p>コンテンツ1</p></div>
</div>
<div class="accordion-item">
<button class="accordion-header">セクション2</button>
<div class="accordion-body"><p>コンテンツ2</p></div>
</div>
</div>
.accordion-body {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease, padding 0.3s ease;
padding: 0 16px;
}
.accordion-body.is-open {
max-height: 400px;
padding: 12px 16px;
}
$(function () {
$('.accordion-header').on('click', function () {
var $body = $(this).next('.accordion-body');
// 他の開いているパネルを閉じる(アコーディオン動作)
$('.accordion-body.is-open').not($body).removeClass('is-open');
$body.toggleClass('is-open');
});
});
まとめ
slideToggle()のカクつき対処ポイントをまとめます。
- padding問題: スライド要素には直接padding不可 → 内側のラッパーで管理
- margin問題: 子要素のmarginをpaddingに変更 or overflow:hiddenで封じ込め
- 推奨: CSSの
max-heightトランジションに移行(GPU加速で滑らか) - アコーディオン: CSSクラスのtoggleClassで実装するとシンプル
関連記事: 「もっと見る」で要素を折りたたむ完全ガイド / CSSアニメーション終了時に処理を行う方法
よくある質問(FAQ)
QslideToggle() の速度を変えたいです。
A
slideToggle(200) のようにミリ秒を指定できます。”slow”(600ms)、”fast”(200ms)の文字列も使えます。カスタムイージングは $.easing や jQuery UIプラグインで追加できます。CSSトランジションを使う場合は transition: max-height 0.3s ease-in-out で指定します。Q画像を含む要素でガタつきます。
A画像のサイズが読み込み前は0で、読み込み後に高さが変わるためガタつきます。CSSで
img { display: block; width: 100%; height: auto; } とaspect-ratio プロパティで縦横比を固定しておくと、読み込み前も適切な高さが確保されます。Q高さが auto の要素でスムーズにアニメーションしたいです。
ACSSでは
height: 0 から height: auto への直接アニメーションはできません。max-height での代替か、JavaScript で実際の高さを取得して設定するパターンを使います:var h = $el.css("display", "block").outerHeight(); $el.hide().animate({ height: h }, 300)Qスクロール中にslideToggleしたとき位置がズレます。
Aアコーディオンを開閉すると上のコンテンツの高さが変わり、スクロール位置がズレることがあります。コンテンツが画面の上部にある場合、開いたパネルが見えない位置に出ることも。ページのどの位置にいても使いやすいよう、開いた後のスクロール位置を調整する実装を検討してください。
Q連続クリックすると変になります。
Aアニメーション中に再クリックされると内部キューが溜まりガタつきます。
$(this).is(":animated") でアニメーション中かチェックするか、.stop(true, true) でキューをクリアしてから実行してください:$(".panel").stop(true, true).slideToggle(300)

