Módulo 01 // Unidad 05

Formularios y validación en cliente

Leer lo que escribe el usuario, comprobarlo antes de enviarlo y explicar qué está mal

Unidad para alumnos del curso

Comprobando tu sesión…

Inicia sesión con la cuenta de Google con la que estás matriculado para ver esta unidad.

Esa cuenta no tiene acceso a este curso. Prueba con otra.

No se ha podido cargar el inicio de sesión de Google. Comprueba la conexión o si algún bloqueador lo impide, y recarga la página.

Los formularios son la vía principal por la que el usuario da datos a una aplicación. JavaScript permite comprobar esos datos antes de enviarlos y señalar los errores en el momento, sin esperar a que responda el servidor. El navegador ya hace buena parte del trabajo por sí mismo; tu código se encarga de lo que el HTML no puede expresar.

1 // Leer los datos de un formulario

Cada campo tiene la propiedad value, y las casillas y botones de opción, checked:

const formulario = document.querySelector('#registro');
const email = formulario.querySelector('#email');
const acepta = formulario.querySelector('#acepta');

email.value;     // 'ana@correo.es'
acepta.checked;  // true o false

Cuando el formulario tiene muchos campos, es más cómodo usar FormData, que recoge de una vez todos los campos que tengan atributo name:

formulario.addEventListener('submit', (e) => {
  e.preventDefault();
  const datos = new FormData(formulario);

  datos.get('email');                       // valor de un campo
  const objeto = Object.fromEntries(datos); // { email: '...', edad: '...' }
});

value siempre es un texto, aunque el campo sea type="number" o type="date". Si necesitas operar con un número, conviértelo con Number(campo.value) o usa campo.valueAsNumber. Sumar sin convertir concatena, como viste en la unidad de coerción.

2 // Validación nativa del navegador

Antes de escribir una línea de JavaScript, el HTML ya permite declarar muchas reglas. El navegador las comprueba al enviar, muestra un mensaje en el idioma del usuario y no deja continuar mientras haya errores:

<form id="registro">
  <input name="nombre" required minlength="2">
  <input name="email" type="email" required>
  <input name="edad" type="number" min="16" max="99">
  <input name="codigo" pattern="[0-9]{5}" title="Cinco dígitos">
  <button>Registrarme</button>
</form>
Atributo Qué comprueba
required Que el campo no esté vacío
type="email", type="url" Que el formato sea válido
minlength, maxlength Longitud del texto
min, max, step Rango de números y fechas
pattern Que el texto completo cumpla una expresión regular

Además, cada campo expone su estado a CSS mediante las pseudoclases :valid e :invalid, y a JavaScript mediante la API de validación.

QUÉ OCURRE AL PULSAR «ENVIAR» usuario pulsa Enviar 1 · navegador required, type... ok 2 · evento submit 3 · tu código reglas propias ok 4 · enviar los datos falla aviso del navegador submit no se dispara falla tus mensajes no se envía nada servidor valida otra vez La validación en cliente mejora la experiencia; la seguridad sólo la da la del servidor.
Fíjate en la primera rama de error: si falla una restricción del HTML, tu listener de submit ni siquiera llega a ejecutarse. Y en la esquina derecha: el camino no termina en el cliente.

3 // Reglas propias con la API de validación

Hay reglas que el HTML no puede expresar: que dos contraseñas coincidan, que una fecha de fin sea posterior a la de inicio o que un nombre de usuario no esté ya usado. Para eso, cada campo tiene métodos y propiedades que se integran con la validación nativa:

Miembro Para qué sirve
campo.checkValidity() Devuelve true si el campo cumple todas sus reglas
campo.validity Objeto con el motivo del error: valueMissing, typeMismatch, tooShort...
campo.validationMessage El mensaje de error que mostraría el navegador
campo.setCustomValidity(texto) Marca el campo como inválido con tu mensaje; con '' lo vuelve válido
formulario.reportValidity() Comprueba todo el formulario y muestra los avisos

La idea de setCustomValidity es que tu regla se comporte igual que una regla nativa: el campo pasa a :invalid, el navegador muestra tu mensaje y el formulario no se envía.

Valida mientras el usuario escribe (evento input) para quitar los errores en cuanto se corrigen, pero muestra los errores nuevos al salir del campo (blur) o al enviar. Marcar en rojo un correo al teclear la primera letra resulta molesto.

4 // La validación en cliente no es seguridad

Todo lo que se ejecuta en el navegador está bajo el control de quien lo usa. Cualquiera puede abrir las herramientas de desarrollo y quitar un required, desactivar JavaScript o enviar la petición al servidor directamente sin pasar por tu formulario.

Por eso la validación en cliente tiene un único objetivo: que el usuario sepa rápido qué tiene que corregir. La protección de los datos corresponde siempre al servidor, que debe volver a comprobar todas las reglas. Es la misma separación que verás en el módulo de desarrollo en entorno servidor.

Un formulario de registro tiene password y repetir. Además de las reglas del HTML, las dos contraseñas deben coincidir, y el error debe desaparecer en cuanto coincidan.

const formulario = document.querySelector('#registro');
const password = formulario.querySelector('[name="password"]');
const repetir = formulario.querySelector('[name="repetir"]');

function comprobarCoincidencia() {
  const mensaje = password.value === repetir.value ? '' : 'Las contraseñas no coinciden';
  repetir.setCustomValidity(mensaje);
}

password.addEventListener('input', comprobarCoincidencia);
repetir.addEventListener('input', comprobarCoincidencia);

formulario.addEventListener('submit', (e) => {
  e.preventDefault();
  const datos = Object.fromEntries(new FormData(formulario));
  console.log('Enviar al servidor:', datos);
});
  1. Una función con la regla: compara los dos valores y fija el mensaje, o lo vacía si coinciden.
  2. Escuchar los dos campos: si sólo escuchas repetir, cambiar después password dejaría un estado incorrecto.
  3. Integración con lo nativo: mientras el mensaje no esté vacío, el campo es :invalid y el navegador bloquea el envío con tu texto, igual que con un required.
  4. El listener de submit queda limpio: cuando se ejecuta, ya se sabe que todas las reglas, nativas y propias, se cumplen.
El formulario sólo dispara submit con datos válidos y el mensaje de error aparece y desaparece solo. Al llegar al servidor, las dos contraseñas deben volver a compararse allí: lo que se ha ganado aquí es rapidez y claridad para el usuario, no seguridad.

5 // Para practicar

Lee todos los tipos de campo con FormData y añade una regla que el HTML no puede expresar. Cada ejercicio trae el enunciado en los comentarios del código.

Formulario con validación propiatraining-daw-sv/modulo-01/05-formularios-y-validacion/01_forms

Autoevaluación

Un campo <input type="number"> contiene 5 y el código hace campo.value + 1. El resultado es '51'. ¿Por qué, si el campo es de tipo número?
value siempre es un texto, sea cual sea el type del campo. El tipo del input sólo cambia el teclado, los controles y la validación, no el valor que lee JavaScript. Hay que convertirlo con Number(campo.value) o usar campo.valueAsNumber.
Tu listener de submit tiene un console.log al principio y no se muestra cuando dejas vacío un campo required. ¿Por qué no se ejecuta tu código?
Porque el navegador comprueba las restricciones del HTML antes de disparar submit. Si alguna falla, muestra su aviso y cancela el envío sin llegar a lanzar el evento. Tu listener sólo se ejecuta cuando todas las restricciones nativas se cumplen.
Si el formulario ya valida en el navegador que el correo tiene formato correcto, ¿por qué el servidor debe comprobarlo de nuevo?
Porque el cliente está bajo el control del usuario. Cualquiera puede desactivar JavaScript, quitar atributos desde las herramientas de desarrollo o enviar la petición directamente sin pasar por el formulario. La validación en cliente sirve para dar respuesta rápida; la que protege los datos es la del servidor.