jQuery / Selector / :checked — チェック済みのチェックボックスとラジオを取得する

jQuery :checkedはチェック済みチェックボックス・ラジオと、選択済みoptionに対応します。チェックボックスだけならinput[type="checkbox"]:checkedのように種類と範囲を限定します。複数値はmapで配列にし、ラジオはnameグループの選択要素から読みます。

チェック状態とvalueを分ける

:checked公式文書はoptionも含むので、フォーム全体の数はチェックボックス数とは限りません。例はchannelsとplanを分け、Priorityのoptionが全体数へ入ることも示します。

チェックボックスの.val()は状態でなく設定valueです。未選択SMSもsmsを返します。一要素は.prop("checked")、集合のどれかは.is(":checked")、該当集合は.filter(":checked")で区別します。複数要素のプロパティgetterは最初だけです。

同じフォームで同じnameのラジオは排他グループです。formとnameへ範囲を限定し、未選択なら集合が空で.val()がundefinedになり得ることを扱います。

チェック・ラジオ・送信値の完全な例

UTF-8のchecked-demo.htmlとして保存し開きます。ネットから完全版jQuery4.0.0を読み、サーバーやビルド不要です。Windows Edgeの4.0.0・3.7.1でキー操作、配列、無効要素、動的changeと復帰を検証しました。画面表示だけで送信しません。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checked controls and form data</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
    fieldset { margin: 12px 0; }
    label { display: block; }
    button, select { font: inherit; max-width: 100%; margin: 4px; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <form id="preferences">
    <fieldset id="channels"><legend>Channels</legend>
      <label><input id="email" type="checkbox" name="channels" value="email" checked>Email</label>
      <label><input id="sms" type="checkbox" name="channels" value="sms">SMS</label>
      <label><input type="checkbox" name="channels" value="archive" checked disabled>Archive (disabled)</label>
      <fieldset disabled><legend>Unavailable</legend>
        <label><input type="checkbox" name="channels" value="legacy" checked>Legacy</label>
      </fieldset>
    </fieldset>
    <fieldset><legend>Plan</legend>
      <label><input id="basic" type="radio" name="plan" value="basic" checked>Basic</label>
      <label><input id="pro" type="radio" name="plan" value="pro">Pro</label>
    </fieldset>
    <label for="priority">Priority</label>
    <select id="priority" name="priority"><option value="normal">Normal</option><option value="high">High</option></select>
    <button type="button" id="choose">Choose SMS and Pro</button>
    <button type="button" id="clear">Clear enabled channels</button>
    <button type="button" id="restore">Reset</button>
  </form>
  <pre id="result" aria-live="polite"></pre>
  <script>
    jQuery(function ($) {
      const $form = $("#preferences"), form = $form[0];
      let changes = 0;
      function boxes() { return $form.find('input[type="checkbox"][name="channels"]'); }
      function values($elements) { return $elements.map(function () { return this.value; }).get(); }
      function render() {
        const $checked = boxes().filter(":checked"), data = new FormData(form);
        $("#result").text("checkedBoxes=" + JSON.stringify(values($checked)) +
          "\nenabledBoxes=" + JSON.stringify(values($checked.filter(":enabled"))) +
          "\nradio=" + ($form.find('input[name="plan"]:checked').val() || "(none)") +
          "\ncheckedAttributes=" + JSON.stringify(values(boxes().filter("[checked]"))) +
          "\nsubmittedChannels=" + JSON.stringify(data.getAll("channels")) +
          "\nform=" + new URLSearchParams(data).toString() +
          "\nglobalChecked=" + $form.find(":checked").length + "\nchanges=" + changes);
      }
      $form.on("change", "input, select", function () { changes++; render(); });
      $("#choose").on("click", function () {
        boxes().filter(":enabled").prop("checked", false);
        $("#sms").prop("checked", true).trigger("change");
        $("#pro").prop("checked", true).trigger("change");
      });
      $("#clear").on("click", function () { boxes().filter(":enabled").prop("checked", false).trigger("change"); });
      $("#restore").on("click", function () { form.reset(); changes = 0; render(); });
      render();
    });
  </script>
</body>
</html>

初期のchannelsは三つチェック済みですが送信可能なのはEmailだけです。globalCheckedはラジオとoption込みで5です。

checkedBoxes=["email","archive","legacy"]
enabledBoxes=["email"]
radio=basic
checkedAttributes=["email","archive","legacy"]
submittedChannels=["email"]
form=channels=email&plan=basic&priority=normal
globalChecked=5
changes=0
  1. SMSをクリックかSpaceで選ぶとsubmittedChannelsは["email","sms"]になり、同じchannels名で二値が入ります。
  2. Proを選ぶとBasicが外れradio=proです。方向キーでもchangeで更新します。
  3. Reset後Choose SMS and Proで有効channelsはSMSだけ、無効Archive・Legacyは残り、radio=pro、changes=2です。
  4. checkedAttributesは既定のemail/archive/legacyのままです。
  5. Clear enabled channelsでenabledBoxesとsubmittedChannelsが空でも無効チェックは残ります。
  6. Resetでチェック・ラジオ・Priority・回数を戻します。

checkedBoxesは現在値、enabledBoxesは:enabledを残した値、submittedChannelsはFormData.getAllです。changesはハンドラー回数で、複数要素へchangeを出すと各々増えます。クリック数ではありません。

属性・プロパティ・更新

[checked]はHTML属性、DOMのcheckedプロパティと:checkedは現在状態です。既定はdefaultCheckedへ対応し、解除しても属性が残る場合があります。[checked]や.attr("checked")の真偽で現在を判断しません。

.prop("checked", trueまたはfalse)は状態を変えますがchangeを自動発生しません。例は明示発火と初期renderを行います。コード設定で報告が変わらなければイベント・描画経路を確認します。

残るformからinput・selectのchangeを委譲します。後から追加してもバブリングを処理し、renderごとにチェックボックスを再検索します。保存済み:checked集合の要素構成が自動更新されるわけではありません。

Resetはform.reset()後にrenderし、idはrestoreでメソッド隠蔽を避けます。既定属性や動的要素の既定を変えれば復帰先も変わります。

無効要素と複数の送信値

disabledな要素や無効fieldset内もチェックは残り得ます。:checkedだけでは送信可能と判定できません。:enabledは継承した無効も扱い、FormDataはArchive・Legacyを除きます。[disabled]がないだけではfieldsetの子を見落とします。

未チェックのチェックボックス・ラジオは通常データから省かれ、false文字列が自動送信されません。value省略の既定はonなので意味ある識別子を指定し、サーバーの欠落規則を決めます。

繰返すnameはFormData.getAll()で複数値を保ちます。Object.fromEntriesで一キーへすると値を失う場合があります。サーバーの配列名規則を別に確認し、許可値と組合せも再検査します。

:checkedは標準querySelectorAllでも使えます。optionだけならoption:checkedやjQuery :selectedへ限定します。装飾でも実input状態とlabelを保ち、見た目だけ選択と違う状態にしません。