【jQuery】別の場所にあるチェックボックスを連動させる完全ガイド|全選択・親子連動・トリプルステート・フォーム送信まで

チェックボックスの「全て選択」ボタンや、親チェックが変わると子チェックも変わる連動処理は、大量の選択肢を扱うフォームでよく使われます。この記事では全選択連動・親子チェック連動・トリプルステート(不確定状態)・フォーム送信時の処理まで解説します。

この記事でわかること

  • 「全て選択」チェックボックスで一括選択・解除する
  • 親チェックで子チェックを全て連動させる
  • 子チェックの状態に応じて親チェックを自動更新する(トリプルステート)
  • チェックボックスの値を取得してフォーム送信する
  • 別の場所にある連動チェックの実装
スポンサーリンク

「全て選択」で一括チェック・解除

<label><input type="checkbox" id="check-all"> 全て選択</label>
<div id="item-list">
  <label><input type="checkbox" class="item-check" value="apple"> りんご</label>
  <label><input type="checkbox" class="item-check" value="banana"> バナナ</label>
  <label><input type="checkbox" class="item-check" value="orange"> オレンジ</label>
</div>
$(function () {
  // 「全て選択」クリック → 子チェックを全て同じ状態にする
  $('#check-all').on('change', function () {
    $('.item-check').prop('checked', $(this).prop('checked'));
  });

  // 子チェックの変更 → 「全て選択」の状態を自動更新
  $('.item-check').on('change', updateAllCheck);

  function updateAllCheck() {
    var total   = $('.item-check').length;
    var checked = $('.item-check:checked').length;

    $('#check-all')
      .prop('checked',      checked === total)
      .prop('indeterminate', checked > 0 && checked < total);
  }
});
indeterminate(不確定状態)で中間状態を表現する
prop("indeterminate", true) を設定すると、チェックボックスがチェック済みでも未チェックでもない「中間」の見た目になります。UIの直感性が上がり、「一部選択中」であることがひと目でわかります。

親子チェックの連動(ツリー構造)

<ul>
  <li>
    <label><input type="checkbox" class="parent-check" data-group="fruits"> 果物</label>
    <ul>
      <li><label><input type="checkbox" class="child-check" data-group="fruits" value="apple"> りんご</label></li>
      <li><label><input type="checkbox" class="child-check" data-group="fruits" value="banana"> バナナ</label></li>
    </ul>
  </li>
  <li>
    <label><input type="checkbox" class="parent-check" data-group="vegs"> 野菜</label>
    <ul>
      <li><label><input type="checkbox" class="child-check" data-group="vegs" value="carrot"> にんじん</label></li>
    </ul>
  </li>
</ul>
$(function () {
  // 親クリック → グループ内の子を全て連動
  $('.parent-check').on('change', function () {
    var group   = $(this).data('group');
    var checked = $(this).prop('checked');
    $('[data-group="' + group + '"].child-check').prop('checked', checked);
  });

  // 子クリック → 親の状態を自動更新(indeterminate対応)
  $('.child-check').on('change', function () {
    var group    = $(this).data('group');
    var $children = $('[data-group="' + group + '"].child-check');
    var total    = $children.length;
    var checked  = $children.filter(':checked').length;

    $('[data-group="' + group + '"].parent-check')
      .prop('checked',      checked === total)
      .prop('indeterminate', checked > 0 && checked < total);
  });
});

別の場所にあるチェックボックスを連動させる

フォームとは別の場所(例: サイドバー)にあるフィルターと、メインコンテンツのチェックを連動するパターンです。

<!-- サイドバーのフィルター -->
<aside id="sidebar">
  <input type="checkbox" id="sidebar-apple" data-target="main-apple"> りんご
</aside>

<!-- メインフォーム -->
<form id="main-form">
  <input type="checkbox" id="main-apple" name="items" value="apple"> りんご
</form>
$(function () {
  // サイドバーのチェック → メインのチェックを連動
  $('#sidebar').on('change', 'input[type=checkbox]', function () {
    var targetId = $(this).data('target');
    $('#' + targetId).prop('checked', $(this).prop('checked'));
  });

  // メインのチェック → サイドバーも連動
  $('#main-form').on('change', 'input[type=checkbox]', function () {
    var id = $(this).attr('id');
    $('[data-target="' + id + '"]').prop('checked', $(this).prop('checked'));
  });
});

チェックされた値を取得してフォーム送信する

$(function () {
  $('form').on('submit', function (e) {
    e.preventDefault();

    // チェックされた値を配列で取得
    var selected = $('.item-check:checked').map(function () {
      return $(this).val();
    }).get();

    if (selected.length === 0) {
      alert('1つ以上選択してください');
      return;
    }

    console.log('選択値:', selected); // ['apple', 'orange'] etc.

    // Ajaxで送信
    $.post('/api/save', { items: selected })
      .done(function (data) {
        console.log('送信成功');
      });
  });
});

まとめ

チェックボックス連動実装のポイントをまとめます。

  • 全選択: prop("checked", value) でまとめてセット
  • 中間状態: prop("indeterminate", true) でトリプルステート
  • 親子連動: data-group でグループ管理
  • 値取得: $(".item:checked").map(fn).get() で配列化

関連記事: チェックボックスを操作する完全ガイド / ラジオボタンの操作完全ガイド / フォームバリデーション完全ガイド

よくある質問(FAQ)

Q動的に追加したチェックボックスに連動が効きません。
Aイベントデリゲーションを使います:$(document).on("change", ".item-check", fn)直接バインドした $(".item-check").on("change", fn) はバインド後に追加された要素には効きません。
Qチェックボックスをプログラムでチェックしてもchangeイベントが発火しません。
Aprop("checked", true) はDOMプロパティを変更しますが、changeイベントは自動発火しません。手動でイベントを発火させるには .trigger("change") を後に呼んでください:$("#checkbox").prop("checked", true).trigger("change")ただし無限ループに注意してください(連動先でもchangeが発火するため)。
Qindeterminate 状態のチェックボックスはフォーム送信でどう扱われますか?
Aindeterminate はCSSの表示状態のみで、実際のチェック状態(true/false)は変わりません。フォーム送信時は checked プロパティの値(true/false)で判断されます。indeterminate のときは通常 checked === false であるため、未チェック扱いになります。
Q全選択後に一部のチェックを外したとき「全て選択」も自動的に外れるようにしたいです。
A本記事の updateAllCheck() 関数がまさにそのパターンです。子チェックの change イベントで全体の状態を再計算し、全チェック = true、一部チェック = indeterminate、ゼロ = false とします。
Qチェックボックスの選択数に上限を設けたいです(最大3つまでなど)。
Achange イベントで選択数を確認し、上限を超えたらチェックを外します:if ($(".item-check:checked").length > 3) { $(this).prop("checked", false); }UIには何個まで選べるかを明示し、上限に達したら未チェックのオプションを disabled にするとよりわかりやすくなります。