jQuery / Seleccionar y desmarcar todas las casillas

Use .prop("checked", true) para seleccionar todas las casillas de un conjunto y .prop("checked", false) para desmarcarlas. Si existe una casilla general, recalcule su estado cuando cambie un elemento. indeterminate indica selección parcial.

Definir conjunto y estados generales

.prop() lee o cambia checked actual. El atributo HTML mediante .attr() no gestiona la elección actual del usuario. Pase booleanos true o false, no cadenas.

El conjunto contiene casillas habilitadas con clase item dentro de #items, sin incluir #all. :enabled excluye controles deshabilitados, incluidos los que heredan ese estado de fieldset. Estar oculto solo por CSS no excluye un control.

La general se marca únicamente si hay al menos un elemento disponible y todos están seleccionados. Parte seleccionada significa checked=false e indeterminate=true; ninguna, ambos false. Cero elementos también deshabilita la general: cero igual a cero no significa selección completa.

Ejemplo completo en un archivo

Guarde todo en UTF-8 como checkbox-demo.html y ábralo en el navegador. Internet carga el archivo oficial de jQuery 4.0.0. No requiere servidor ni paquetes. Las funciones principales se ejecutaron en Edge para Windows con 3.7.1 y 4.0.0.

Alpha empieza marcada y Beta no. Locked está marcada pero deshabilitada, fuera del cambio y recuento. La general carece de name y no se envía como valor de elemento.

<!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>

Salida inicial: uno seleccionado de dos disponibles. La general muestra estado parcial.

selected=1/2
master checked=false
master mixed=true
item changes=0
values=alpha
  1. Select all available items da selected=2/2, master checked=true y master mixed=false. Otra activación da 0/2; Locked sigue marcada.
  2. Solo Beta da 1/2 y master mixed=true. Seleccionar también Alpha completa la general. Las etiquetas y Espacio sobre una casilla enfocada también funcionan.
  3. Tras seleccionar todo, Add item añade Extra 1 sin marcar: 2/3 y parcial. Márquela para volver a selección completa.
  4. Remove items da 0/0 y deshabilita la general. Añadir un elemento vuelve a habilitarla.
  5. Con Alpha y Beta, Inspect submission muestra item=alpha&item=beta. Locked queda fuera; sin selección disponible aparece (no successful controls).
  6. Reset example recrea tres elementos iniciales y limpia contadores y salida. No envía nada y deshace los cambios.

Sincronizar cambios individuales y dinámicos

El controlador change general asigna this.checked a los disponibles y llama sync() una vez. .prop() no dispara change de cada hijo. item changes crece por cambios individuales, no por selección general.

La delegación en #items estable atiende incorporaciones sin enlazar cada casilla de nuevo. available() consulta el DOM actual cada vez. Una colección guardada al inicio podría omitir añadidos o conservar eliminados.

Añadir y eliminar llaman explícitamente sync(). La delegación no observa cambios estructurales del DOM. Si otro código cambia disabled o checked, conéctelo con esta sincronización. Esas modificaciones no se detectan automáticamente aquí.

Si un cambio individual también calcula precios, diseñe ese trabajo para operaciones generales. .trigger("change") puede servir de forma selectiva, evitando recalcular todo o duplicar peticiones por cada elemento. Aquí se sincroniza una vez tras la asignación conjunta.

Estado parcial, reinicio y envío

indeterminate es un estado visual establecido con JavaScript, no atributo HTML inicial ni valor adicional enviado. Es independiente de checked. sync() asigna ambos para no dejar indicadores antiguos. Etiquetas reales y casillas nativas conservan el teclado normal.

Inspect submission muestra FormData sin petición de red. Controles habilitados marcados aportan nombres y valores. Si el negocio exige datos deshabilitados, diseñe su representación y validación del servidor explícitamente. La selección cliente no sustituye permisos del servidor.

Reset example es una recreación personalizada. El reset nativo restaura valores predeterminados; después debe recalcularse la general. Cambiar solo a type="reset" no eliminaría incorporaciones ni reiniciaría contadores del mismo modo.

Se seleccionan elementos presentes en el documento. Filas de otras páginas servidor aún no cargadas requieren estado servidor aparte. Separe cada grupo. Si hay filtro, defina si «todo» son los visibles o todos los habilitados del grupo, y ajuste selectores y etiquetas.