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 falseCuando 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.
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);
});- Una función con la regla: compara los dos valores y fija el mensaje, o lo vacía si coinciden.
- Escuchar los dos campos: si sólo escuchas
repetir, cambiar despuéspassworddejaría un estado incorrecto. - Integración con lo nativo: mientras el mensaje no esté vacío, el campo es
:invalidy el navegador bloquea el envío con tu texto, igual que con unrequired. - El listener de
submitqueda limpio: cuando se ejecuta, ya se sabe que todas las reglas, nativas y propias, se cumplen.
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_formsAutoevaluació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?
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.