jQuery / Select and Clear All Checkboxes

Use .prop("checked", true) or .prop("checked", false) on a target collection to select or clear all checkboxes. When a master checkbox controls individual items, also recalculate its state whenever an item changes. Use indeterminate to indicate a partial selection.

Define the eligible set and master states

.prop() reads or changes the current checked property. Do not use the HTML checked attribute through .attr() to manage current user choices. Pass Boolean true or false rather than strings.

Here the set is enabled checkboxes with class item inside #items. The master #all is outside that set. :enabled excludes disabled controls, including those disabled through a fieldset. Being hidden by CSS alone does not exclude a control.

Check the master only when at least one eligible item exists and all are checked. A partial selection sets checked=false and indeterminate=true. None selected means both false. Zero eligible items also disables the master, avoiding an incorrect “all selected” result from zero equaling zero.

Complete single-file example

Save everything as UTF-8 checkbox-demo.html and open it in a browser. Internet loads the official jQuery file 4.0.0. No server or installation is required. Core behavior was executed in Windows Edge with jQuery 3.7.1 and 4.0.0.

Alpha begins checked and Beta unchecked. Locked is checked but disabled, so it is excluded from the bulk operation and count. The master has no name and is not submitted as an item value.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Select all eligible checkboxes</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; }
    label { display: block; padding: 6px; }
    button { font: inherit; margin: 4px; }
    fieldset { margin: 12px 0; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <form id="choices">
    <label><input type="checkbox" id="all"> Select all available items</label>
    <fieldset id="items"><legend>Items</legend></fieldset>
    <button type="button" id="add">Add item</button>
    <button type="button" id="clear">Remove items</button>
    <button type="button" id="reset">Reset example</button>
    <button type="submit">Inspect submission</button>
  </form>
  <pre id="result" aria-live="polite"></pre>
  <pre id="submission"></pre>
  <script>
    $(function () {
      const $form = $("#choices"), $all = $("#all");
      let next = 1, itemChanges = 0;
      function available() { return $("#items input.item:checkbox:enabled"); }
      function sync() {
        const $items = available(), total = $items.length;
        const count = $items.filter(":checked").length;
        $all.prop({ checked: total > 0 && count === total,
          indeterminate: count > 0 && count < total, disabled: total === 0 });
        const values = $items.filter(":checked").map(function () { return this.value; }).get();
        $("#result").text(`selected=${count}/${total}\nmaster checked=${$all.prop("checked")}\nmaster mixed=${$all.prop("indeterminate")}\nitem changes=${itemChanges}\nvalues=${values.join(",")}`);
      }
      function addItem(value, text, checked, disabled) {
        const label = document.createElement("label"), input = document.createElement("input");
        input.type = "checkbox"; input.name = "item"; input.className = "item";
        input.value = value; input.checked = checked; input.disabled = disabled;
        label.append(input, document.createTextNode(" " + text));
        document.getElementById("items").append(label);
      }
      $all.on("change.selectAllLesson", function () {
        available().prop("checked", this.checked);
        sync(); // Setting checked does not emit change on each item.
      });
      $("#items").on("change.selectAllLesson", "input.item:checkbox", function () {
        itemChanges++; sync();
      });
      $("#add").on("click", function () {
        addItem("extra" + next, "Extra " + next, false, false); next++; sync();
      });
      $("#clear").on("click", function () { $("#items label").remove(); sync(); });
      $("#reset").on("click", function () {
        $("#items label").remove(); $("#items").prop("disabled", false);
        addItem("alpha", "Alpha", true, false);
        addItem("beta", "Beta", false, false);
        addItem("locked", "Locked (unavailable)", true, true);
        next = 1; itemChanges = 0; $("#submission").empty(); sync();
      });
      $form.on("submit", function (event) {
        event.preventDefault();
        const params = new URLSearchParams(new FormData(this));
        $("#submission").text(params.toString() || "(no successful controls)");
      });
      $("#reset").trigger("click");
    });
  </script>
</body>
</html>

Initial output is one selected out of two eligible items. The master shows a mixed state.

selected=1/2
master checked=false
master mixed=true
item changes=0
values=alpha
  1. Activate Select all available items: selected=2/2, master checked=true, master mixed=false. Activate again for 0/2. Locked remains checked.
  2. Select only Beta for 1/2 and master mixed=true. Select Alpha as well to check the master. Labels and Space on a focused checkbox work too.
  3. With all selected, Add item adds unchecked Extra 1: 2/3 and mixed. Select Extra 1 for full selection again.
  4. Remove items produces 0/0 and disables the master. Adding a new item enables it again.
  5. With Alpha and Beta checked, Inspect submission shows item=alpha&item=beta. Disabled Locked is excluded. With no eligible selection it shows (no successful controls).
  6. Reset example recreates the original three items and clears counters and output. It sends nothing and removes added items and selection changes.

Synchronize individual and dynamic changes

The master’s change handler applies this.checked to eligible items and calls sync() once. Assigning .prop() does not dispatch each child’s change event. Consequently item changes increases for individual changes, not bulk selection.

Delegation on the stable #items handles later additions without binding each checkbox separately. available() queries the current DOM each time. A collection cached at startup could miss additions or retain removed items.

Addition and removal code explicitly calls sync(). Event delegation does not watch structural DOM changes. If other code changes disabled or checked, connect it to the same synchronization function. Such programmatic changes are not automatically detected here.

If individual changes also recalculate prices or perform other work, design an explicit bulk path for that work. Selective .trigger("change") is possible, but avoid recalculating the whole set or sending duplicate requests for every item. This example synchronizes once after bulk assignment.

Mixed state, reset and submitted values

indeterminate is a JavaScript-controlled visual state, not an initial HTML attribute or a separate submitted value. It is independent of checked. sync() sets both properties so an old mixed indicator cannot remain. Real labels and native checkboxes retain normal keyboard interaction.

Inspect submission reads FormData and displays it; no network request is made. Checked enabled controls contribute names and values. If unavailable values must be included for business reasons, design their representation and server validation explicitly. Client selection never replaces server permission checks.

Reset example is a custom recreation button. Native form reset is a different operation that restores defaults; recalculate the master after that operation finishes. Merely changing this button to type="reset" would not reproduce dynamic-item removal and counter initialization.

This selects items present in the document. Selecting server-paginated rows that have not loaded needs separate server-side state. Scope each group separately. If filtering is involved, decide whether “all” means visible items or every enabled item in the group, then match selectors and labels to that policy.