> Manuales > Taller de Javascript

Cómo realizar la validación de casillas de verificación, de modo que podamos definir grupos de checkbox y mediante Javascript asegurarnos que no se marque más de un número de casillas permitidas en cada grupo.

En muchas ocasiones tenemos que validar el estado de casillas de verificación de formularios (elementos checkbox). Este tipo de controles de formulario son un tanto especiales y su validación es distinta que otros elementos más clásicos como los campos input.

Así que en este artículo os dejo unas líneas de mi cosecha, en la creación de un script Javascript que he tenido que hacer para comprobar el estado de elementos checkbox o casillas de verificación de formularios.

Nota: en las tareas de actualización de este artículo vamos a colocar al final un código alternativo de solución de este ejemplo que lleva a cabo unas prácticas más actuales y consideradas como más recomendables. Puedes ver esa solución al final.

Se trata de utilizar las típicas casillas de verificación pero con un limitador de grupo. Se puede utilizar en quinielas de varios resultados, en los futuros test de las autoescuelas con la posibilidad de marcar varias respuestas, etc.

Formulario con los checkbox

Tenemos una serie de grupos de checkbox y lo que queremos hacer es asegurarnos que en cada grupo, de manera independiente, no se hayan marcado más de un número definido de casillas.

Por ejemplo, tenemos x grupos de 3 casillas de verificación cada uno. Si el usuario marca una casilla de casillas de uno de los grupos no pasa nada. Si marca 2 casillas tampoco pasa nada, pero si intenta marcar los tres checkbox del grupo Javascript no lo permite y muestra un mensaje de error.

Vamos a tener un formulario con, en este caso, dos grupos de casillas de verificación.

<form action="" method="post" enctype="multipart/form-data" name="formulario" id="formulario">
    <table width="76">
        <tr>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox1, 0)' name='checkbox1' value='checkbox1'>
            </td>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox2, 0)' name='checkbox2' value='checkbox2'>
            </td>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox3, 0)' name='checkbox3' value='checkbox3'>
            </td>
        </tr>
        <tr>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox4, 1)' name='checkbox4' value='checkbox4'>
            </td>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox5, 1)' name='checkbox5' value='checkbox5'>
            </td>
            <td width='20' valign='top'>
                <input type='checkbox' onclick='validar(formulario.checkbox6, 1)' name='checkbox6' value='checkbox6'>
            </td>
        </tr>
    </table>
</form>

Como podemos ver, el nombre de cada casilla es distinto. Y además tenemos una función que se ejecuta cuando se pulsa sobre el checkbox (evento onclick), que será la encargada de realizar la verificación.

Función para verificar checkbox por grupos

Veamos el código javascript que utilizamos para realizar la comprobación de que varios checkbox no puedan estar pulsados a la vez en el mismo grupo.

Primero definimos un par de variables globales, que utilizaremos para definir las casillas máximas que pueden estar marcadas al mismo tiempo, y otra para llevar la cuenta de las casillas que hay marcadas en cada grupo.

// Número máximo de casillas marcadas por cada fila
var maxi = 2;

// El contador es un array donde cada posición representa una línea del formulario
var contador = [0, 0];

Ahora la función que realizará la cuenta de casillas e informará de un posible fallo en la comprobación, si se pulsan más que las que se debe.

function validar(check, grupo) {
    // Compruebo si la casilla está marcada
    if (check.checked) {
        // Está marcada, entonces aumento en uno el contador del grupo
        contador[grupo]++;
        // Compruebo si el contador ha llegado al máximo permitido
        if (contador[grupo] > maxi) {
            // Si ha llegado al máximo, muestro mensaje de error
            alert('No se pueden elegir más de ' + maxi + ' casillas a la vez.');
            // Desmarco la casilla, porque no se puede permitir marcar
            check.checked = false;
            // Resto una unidad al contador de grupo, porque he desmarcado una casilla
            contador[grupo]--;
        }
    } else {
        // Si la casilla no estaba marcada, resto uno al contador de grupo
        contador[grupo]--;
    }
}

La función recibe dos parámetros. Primero el campo de formulario checkbox que se ha pulsado. Luego el número de grupo al que pertenece ese checkbox.

El checkbox lo necesita para conocer su estado y para cambiarlo si fuera necesario. El grupo lo utiliza para saber a qué contador debe referirse, para saber el número de casillas que hay pulsadas en ese grupo.

La función está comentada para facilitar su lectura y comprensión.

Verificación de checkboxes por grupos más actual

Ahora vamos a resolver este mismo problema de validación de checkboxes de una manera más avanzada. Vamos a ver cómo mejorar la forma de manejar los eventos y la validación de los checkboxes utilizando prácticas modernas de JavaScript, como el uso de event listeners en lugar de manejar eventos directamente en el HTML. También podemos asegurarnos de que el código sea más modular y fácil de mantener.

Para mostrar esta solución tenemos un código HTML completo, que incluye la estructura del formulario y el script de JavaScript usando técnicas actuales:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Validación de Checkbox por Grupo</title>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data" name="formulario" id="formulario">
        <table>
            <tr>
                <td>
                    <input type='checkbox' name='checkbox1' value='checkbox1'>
                </td>
                <td>
                    <input type='checkbox' name='checkbox2' value='checkbox2'>
                </td>
                <td>
                    <input type='checkbox' name='checkbox3' value='checkbox3'>
                </td>
            </tr>
            <tr>
                <td>
                    <input type='checkbox' name='checkbox4' value='checkbox4'>
                </td>
                <td>
                    <input type='checkbox' name='checkbox5' value='checkbox5'>
                </td>
                <td>
                    <input type='checkbox' name='checkbox6' value='checkbox6'>
                </td>
            </tr>
        </table>
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const formulario = document.getElementById('formulario');
            const maxCheckboxesAllowed = 2;
            const contador = [0, 0];

            formulario.addEventListener('change', function (event) {
                const element = event.target;
                if (element.type === 'checkbox') {
                    // Determinar el grupo (fila) al que pertenece el checkbox
                    const grupo = Array.from(formulario.querySelectorAll('tr')).findIndex(row => row.contains(element));
                    if (element.checked) {
                        contador[grupo]++;
                        if (contador[grupo] > maxCheckboxesAllowed) {
                            alert('No se pueden elegir más de ' + maxCheckboxesAllowed + ' casillas a la vez.');
                            element.checked = false;
                            contador[grupo]--;
                        }
                    } else {
                        contador[grupo]--;
                    }
                }
            });
        });
    </script>
</body>
</html>

Cambios y mejoras:

  1. JavaScript Moderno: Utilizo addEventListener para manejar el evento de cambio en el formulario. Esto separa la lógica de JavaScript del HTML, haciendo que el código sea más fácil de mantener y evita la contaminación del espacio global de nombres.
  2. Determinación Dinámica del Grupo: En lugar de pasar el grupo manualmente, el script identifica a qué grupo (fila) pertenece un checkbox de manera dinámica basado en su posición en el formulario. Esto hace que la distribución de los componentes en el HTML marque la definción de los grupos de checkboxes. Puede ser más adaptable a cambios en el HTML, que no repercuten en el cambio del Javascript, pero tienes que tener cuidado porque también estamos acoplando el javascript a cómo está construido el HTML del formulario.
  3. Escucha en el Formulario: En lugar de poner un manejador en cada checkbox, pongo un único manejador en el formulario que escucha cambios en cualquier checkbox. Esto es más eficiente y fácil de manejar.

Usar un atributo data-group para poder marcar el grupo de los elementos

El enfoque anterior es más limpio aunque quizás se ha complicado un poco la selección de los checkboxes que pertenecen a un mismo grupo. No me gusta mucho por el hecho de acoplarte a cómo está construido el HTML, porque si se cambia el HTML tu javascript puede dejar de funcionar.

Creo que sería mejor idenfificar el grupo al que pertenecen los checkboxes mediante otra técnica. Podrías usar un data-group para indicar el grupo y quedaría limpio y más adaptable.

Asi que vamos a ver otra alternativa que sería todavía mejor. De esta manera, el JavaScript se desacopla del diseño específico del HTML, y los cambios en la estructura del formulario no afectarían la funcionalidad del script.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Validación de Checkbox por Grupo</title>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data" name="formulario" id="formulario">
        <table>
            <tr>
                <td>
                    <input type='checkbox' data-group="0" name='checkbox1' value='checkbox1'>
                </td>
                <td>
                    <input type='checkbox' data-group="0" name='checkbox2' value='checkbox2'>
                </td>
                <td>
                    <input type='checkbox' data-group="0" name='checkbox3' value='checkbox3'>
                </td>
            </tr>
            <tr>
                <td>
                    <input type='checkbox' data-group="1" name='checkbox4' value='checkbox4'>
                </td>
                <td>
                    <input type='checkbox' data-group="1" name='checkbox5' value='checkbox5'>
                </td>
                <td>
                    <input type='checkbox' data-group="1" name='checkbox6' value='checkbox6'>
                </td>
            </tr>
        </table>
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const formulario = document.getElementById('formulario');
            const maxCheckboxesAllowed = 2;
            const contador = [0, 0];

            formulario.addEventListener('change', function (event) {
                const element = event.target;
                if (element.type === 'checkbox') {
                    const grupo = element.getAttribute('data-group');
                    if (element.checked) {
                        contador[grupo]++;
                        if (contador[grupo] > maxCheckboxesAllowed) {
                            alert('No se pueden elegir más de ' + maxCheckboxesAllowed + ' casillas a la vez en el grupo ' + (parseInt(grupo) + 1) + '.');
                            element.checked = false;
                            contador[grupo]--;
                        }
                    } else {
                        contador[grupo]--;
                    }
                }
            });
        });
    </script>
</body>
</html>

Mejoras Implementadas:

  1. Atributo data-group: Cada checkbox tiene un atributo data-group que indica a qué grupo pertenece, eliminando la dependencia del layout HTML.
  2. Flexibilidad y Mantenimiento: Esta solución es más flexible y fácil de mantener porque los cambios en la estructura del HTML no afectan el JavaScript siempre que el atributo data-group esté presente y sea correcto.
  3. Mejora de Mensajes: Esto nos permite incluso hacer cosas como agregar datis al mensaje de alertam, para indicar de qué grupo se está excediendo el límite.

Javier Bernal Lérida

Manual