Table of Contents
¿Por qué la validación de formularios avanzados importa
La validación de formularios va mucho más allá de los campos simples y limitaciones. Mientras que los atributos HTML5 proporcionan una base sólida, a menudo se reducen cuando usted necesita aplicar reglas complejas de negocio, proporcionar retroalimentación en tiempo real, o crear una experiencia de usuario suave en diferentes navegadores. La validación basada en JavaScript le da control completo sobre cuándo y cómo funciona la validación, permitiendo implementar reglas personalizadas como medidores de la lógica de la contraseña,
La validación de cliente también reduce la carga del servidor y acelera el bucle de retroalimentación para los usuarios. En lugar de enviar un formulario y esperar una respuesta del servidor, los errores se detectan inmediatamente. Sin embargo, es fundamental recordar que la validación de lado del cliente es una ]conveniencia, no una medida de seguridad.
En esta guía, vamos a caminar a través de la construcción de un sistema de validación avanzado completo utilizando vainilla JavaScript. Aprenderás a definir reglas personalizadas, mostrar mensajes de error dinámicos, validar varios campos juntos y mantener tu código mantenible. Cada sección incluye fragmentos de códigos de producción que puedes adaptar inmediatamente.
Comprender los límites de la validación HTML5
Atributos HTML5 como , , y son grandes para simples cheques, pero carecen de flexibilidad. Por ejemplo:
- No se puede confirmar que un campo de confirmación y contraseña coincida con HTML5 solo.
- Mensajes de error recibidos – Las herramientas incorporadas del navegador son inconsistentes en todos los navegadores y no pueden ser estilos completamente.
- No hay actualización en tiempo real – La validación HTML5 normalmente dispara solamente en la presentación de formularios, no en cada pulsación.
- API de validación de datos – Aunque es poderoso, sigue dependiendo de las reglas incorporadas del navegador y carece de la capacidad de definir la lógica personalizada compleja fácilmente.
JavaScript llena estos vacíos. Con los oyentes de eventos y las funciones personalizadas, puedes crear validación que sea expresiva y fácil de usar.
Configuración de la estructura de formularios HTML
Comience con una forma semántica limpia. Aquí está un ejemplo que incluye campos para el nombre de usuario, el correo electrónico, la contraseña y confirmar la contraseña. Hemos añadido atributos para la accesibilidad y elementos vacíos que mantendrán mensajes de error.
<form id="registrationForm" novalidate>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" placeholder="e.g. johndoe" required>
<span id="usernameError" class="error-message" role="alert"></span>
</div>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" id="email" name="email" placeholder="[email protected]" required>
<span id="emailError" class="error-message" role="alert"></span>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="At least 8 characters" required>
<span id="passwordError" class="error-message" role="alert"></span>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input type="password" id="confirmPassword" name="confirmPassword" placeholder="Repeat password" required>
<span id="confirmPasswordError" class="error-message" role="alert"></span>
</div>
<button type="submit">Register</button>
</form>
Observe el atributo ] en el formulario. Esto le dice al navegador que apague su propia validación integrada para que podamos manejar todo con JavaScript. Utilizaremos CSS para mostrar o ocultar mensajes de error y añadir comentarios visuales (por ejemplo, bordes rojos) a campos inválidos.
Diseño de una validación central
En lugar de dispersar código de validación a través de los manipuladores de eventos, creamos un objeto central que contiene reglas personalizadas. Cada regla es una función que devuelve (valida) o (inválido) junto con un mensaje de error opcional. Este enfoque hace que el código sea fácil de extender y mantener.
const validators = {
required: (value) => value.trim() !== '' || 'This field is required.',
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || 'Please enter a valid email address.',
minLength: (min) => (value) => value.length >= min || `Must be at least ${min} characters.`,
passwordStrength: (value) => {
const errors = [];
if (value.length < 8) errors.push('At least 8 characters.');
if (!/[A-Z]/.test(value)) errors.push('One uppercase letter.');
if (!/[0-9]/.test(value)) errors.push('One number.');
if (!/[!@#$%^&*]/.test(value)) errors.push('One special character.');
return errors.length === 0 || errors.join(' ');
},
match: (otherFieldId) => (value, formData) => {
const otherValue = formData.get(otherFieldId);
return value === otherValue || 'Passwords do not match.';
}
};
Observe que los validadores pueden ser parametizados – devuelve una función que espera el valor. El validador recibe la id de otro campo y utiliza (un objeto) para comparar valores. Este patrón es altamente reutilizable.
Creación de reglas personalizadas para cada campo
Define qué validadores se aplican a cada campo. Esta asignación es un objeto simple donde las claves son nombres de campo y los valores son arrays de funciones validadoras.
const fieldRules = {
username: [
validators.required,
validators.minLength(3),
(value) => /^[a-zA-Z0-9_]+$/.test(value) || 'Usernames can only contain letters, numbers, and underscores.'
],
email: [
validators.required,
validators.email
],
password: [
validators.required,
validators.passwordStrength
],
confirmPassword: [
validators.required,
validators.match('password')
]
};
Puede agregar fácilmente una nueva regla personalizada, como un cheque que el nombre de usuario no se toma ya (validación asinc), añadiendo una función asinc a la lista. Por ahora, mantendremos todas las reglas sincrónicas.
Validación de ejecución en la presentación de formularios
Adjunte un oyente de eventos al formulario que previene la presentación, ejecuta todos los validadores y recopila errores. Si no se encuentran errores, se permite presentar el formulario.
const form = document.getElementById('registrationForm');
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
const errors = validateForm(formData);
if (Object.keys(errors).length === 0) {
// All valid – you can submit programmatically or send via fetch
console.log('Form is valid. Submitting...');
// form.submit();
} else {
displayErrors(errors);
}
});
La función se realiza sobre cada campo, ejecuta sus validadores y recopila mensajes de error. Aquí está la implementación:
function validateForm(formData) {
const errors = {};
for (const [fieldName, rules] of Object.entries(fieldRules)) {
const value = formData.get(fieldName) || '';
for (const rule of rules) {
const result = rule(value, formData);
if (result !== true) {
errors[fieldName] = result; // result is the error string
break; // stop after first failure for this field
}
}
}
return errors;
}
Mostrando la retroalimentación en tiempo real
Los usuarios se benefician de ver errores como escriben, no sólo en el envío. Agrega los oyentes de eventos para y en cada campo. Para evitar usuarios abrumadores, un buen patrón es validar el borrón (cuando salen del campo) y luego revalidar en cada entrada posterior hasta que el error se resuelva.
document.querySelectorAll('#registrationForm input').forEach((input) => {
input.addEventListener('blur', () => {
validateField(input.id, input.value);
});
input.addEventListener('input', () => {
// If the field currently has an error, re-validate on each keystroke
const errorSpan = document.getElementById(input.id + 'Error');
if (errorSpan.textContent !== '') {
validateField(input.id, input.value);
}
});
});
La función verifica sólo las reglas para un campo único y actualiza el intervalo de error correspondiente.
function validateField(fieldId, value) {
const formData = new FormData(form);
formData.set(fieldId, value);
const rules = fieldRules[fieldId];
if (!rules) return;
for (const rule of rules) {
const result = rule(value, formData);
if (result !== true) {
setFieldError(fieldId, result);
return;
}
}
clearFieldError(fieldId);
}
Styling Errores y Estados de éxito
Utilice CSS para cambiar la apariencia visual de campos válidos/inválidos. Las funciones y añaden o eliminan clases de CSS y actualizan el atributo para la accesibilidad.
function setFieldError(fieldId, message) {
const input = document.getElementById(fieldId);
const errorSpan = document.getElementById(fieldId + 'Error');
input.classList.add('is-invalid');
input.classList.remove('is-valid');
input.setAttribute('aria-invalid', 'true');
errorSpan.textContent = message;
}
function clearFieldError(fieldId) {
const input = document.getElementById(fieldId);
const errorSpan = document.getElementById(fieldId + 'Error');
input.classList.remove('is-invalid');
input.classList.add('is-valid');
input.setAttribute('aria-invalid', 'false');
errorSpan.textContent = '';
}
El CSS correspondiente podría ser:
.is-invalid {
border-color: #dc3545;
}
.is-valid {
border-color: #28a745;
}
.error-message {
color: #dc3545;
font-size: 0.875rem;
min-height: 1.2em;
}
Construyendo una contraseña de fuerza de la máquina
Un medidor de fuerza de contraseña proporciona una retroalimentación visual que anima a los usuarios a crear contraseñas más fuertes. En lugar de un simple pase/fail, puede calcular una puntuación y mostrar una barra de progreso.
function passwordStrengthScore(password) {
let score = 0;
if (password.length >= 8) score += 1;
if (password.length >= 12) score += 1;
if (/[A-Z]/.test(password)) score += 1;
if (/[a-z]/.test(password)) score += 1;
if (/[0-9]/.test(password)) score += 1;
if (/[^A-Za-z0-9]/.test(password)) score += 1;
return score; // 0-6
}
Adjunte un evento al campo de contraseña que actualiza un elemento medidor. Puede mapear la puntuación a una etiqueta como Weak (0-2), Fair (3-4), Strong (5-6).
const passwordMeter = document.getElementById('passwordStrengthMeter');
const passwordInput = document.getElementById('password');
passwordInput.addEventListener('input', () => {
const score = passwordStrengthScore(passwordInput.value);
const percentage = (score / 6) * 100;
passwordMeter.value = percentage;
passwordMeter.style.accentColor = score < 3 ? '#dc3545' : score < 5 ? '#ffc107' : '#28a745';
});
Reglas Aduanas Asincrónicas
Algunas comprobaciones de validación requieren una ida y vuelta al servidor, como comprobar si un nombre de usuario o correo electrónico ya está registrado. Para manejar esto sin bloquear la interfaz de usuario, puede hacer que su validador una función asinc y ajustar la lógica de validación para esperar el resultado.
const asyncValidators = {
uniqueUsername: async (value) => {
try {
const response = await fetch(`/api/check-username?username=${encodeURIComponent(value)}`);
const data = await response.json();
return data.available || 'Username is already taken.';
} catch {
return 'Could not verify username availability.';
}
}
};
Luego, actualice para apoyar a validadores de asinc. También debe manejar el estado de carga para evitar múltiples solicitudes simultáneas (deber la solicitud).
Buenas prácticas para los formularios de producción
- Siempre validar el lado del servidor. La validación del lado del cliente es una comodidad, nunca una capa de seguridad. Utilice las mismas reglas en el backend para rechazar datos maliciosos o malformados.
- Debounce async checks. Si validas el teclado para llamadas de servidor, usa un temporizador de despresivo (por ejemplo, 300 ms) para evitar martillar el servidor.
- ]Utilice la API de validación de limitaciones cuando sea posible. Puede mezclar JavaScript personalizado con los métodos incorporados y para un enfoque híbrido. Esto es útil para campos que tienen limitaciones estándar pero necesitan mensajes personalizados.
- Hacer que la validación sea accesible. Usar para asociar mensajes de error con entradas. Actualizar y regiones para que los lectores de pantalla anuncien errores.
- Prueba los navegadores y dispositivos. Manejar eventos táctiles, teclados virtuales y diferentes tamaños de pantalla es esencial. También prueba con el navegador de autofill, que puede disparar eventos en pedidos inesperados.
- Mantén el rendimiento en mente. Para formas complejas con muchos campos, validación de lotes o uso para actualizaciones de la interfaz de usuario puede prevenir la tobina.
- Proveer mensajes de error claros y constructivos. Diga al usuario exactamente lo que es incorrecto y cómo solucionarlo. Evite mensajes genéricos como "Invalide input." En lugar de decir "Password debe incluir al menos un número."
Seguir adelante: Validación modular con bibliotecas
Si su proyecto requiere un alto nivel de reglas personalizadas y validación en tiempo real, usted podría considerar una biblioteca ligera como Validate.js o la API de validación de la prueba ]. Sin embargo, el enfoque de vainilla JavaScript demostrado en este artículo le da control completo y cero dependencias que son ideales para diseñar aplicaciones específicas.
También puede combinar validación personalizada con marcos modernos como React o Vue, pero los conceptos básicos de separar reglas de la presentación siguen siendo los mismos.
Pitfalls comunes para evitar
- Validando solamente en el envío. Los usuarios prefieren la retroalimentación inmediata. Al mínimo, validan el desdibujo para cada campo.
- No se reinician errores cuando los campos se vuelven válidos. Una vez que un usuario corrige un error, despeja el mensaje. La lógica en tiempo real de este artículo maneja automáticamente.
- Suponiendo que el usuario obedecerá la validación. Puede desactivarse completamente JavaScript. Siempre respalda cada comprobación con la lógica del lado del servidor.
- Reglas de aplicación . Mantener los validadores simples y enfocados. Una función debe probar una preocupación. Combinarlos para aplicar múltiples criterios.
Conclusión
Construyendo validación personalizada de forma JavaScript es una habilidad esencial para crear aplicaciones web pulidas y fáciles de usar. Si estructura tu código en torno a funciones de validador reutilizables y retroalimentación en tiempo real, puedes manejar todo desde campos básicos requeridos hasta contadores de fuerza de contraseña complejos y comparaciones de campo cruzado. Los ejemplos proporcionados en este artículo te dan una base sólida para adaptarse a tus propios proyectos.
Recuerde que la validación es una parte crítica de la experiencia del usuario. Pruebe sus formularios a fondo, escuche la retroalimentación del usuario, y siempre empareja la validación del lado del cliente con controles del lado del servidor robustos. Para más información, consulte la W3C Guía de la Iniciativa de Accesibilidad Web sobre validación de formularios y el artículo ].
Siguiendo los patrones y las mejores prácticas aquí delineadas, crearás formas que sean seguras y agradables de usar. Comience experimentando con los fragmentos de código, personalice las reglas para que coincida con su lógica de negocio, y se dirija sobre la base de interacciones reales de los usuarios.