【jQuery】slideToggle()がカクつく・ガタつく原因と対処法完全ガイド|paddingマージン・CSSトランジション・アコーディオンまで

【jQuery】slideToggle()がカクつく原因とスムーズに動作させる対処法 jQuery

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-topmargin-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() の速度を変えたいです。
AslideToggle(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)