Skip to content

L70 - Formularios

Los formularios son el principal medio de interacción del usuario con una web. JavaScript permite controlar el envío, validar los datos y mejorar la experiencia de usuario sin recargar la página.

// Por ID
const formulario = document.getElementById('form-login');
// Por selector CSS
const form = document.querySelector('form');
// Desde document.forms (HTMLCollection)
const primerForm = document.forms[0];
const formNamed = document.forms['login']; // <form name="login">
// Acceder a los campos del formulario
console.log(formulario.elements); // HTMLFormControlsCollection
console.log(formulario.elements[0]); // primer campo
console.log(formulario.elements['email']); // campo con name="email"

El evento submit se dispara cuando se intenta enviar un formulario (clic en botón submit, Enter en un campo).

formulario.addEventListener('submit', (e) => {
e.preventDefault(); // 🚫 Evita la recarga de la página
console.log('Formulario enviado (sin recargar)');
// Procesar los datos...
});

⚠️ Siempre llamar a e.preventDefault() si vas a manejar el envío con JS. Si no, el navegador recargará la página (comportamiento por defecto).

FormData es un objeto que captura todos los campos de un formulario de forma automática:

formulario.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(formulario);
// Leer valores individuales
console.log(formData.get('email')); // valor del campo name="email"
console.log(formData.get('password'));
// Checkboxes y radios
console.log(formData.get('suscripcion')); // 'on' si está marcado (sin value)
// Múltiples selecciones (select multiple)
console.log(formData.getAll('intereses')); // ['deportes', 'musica']
// Convertir a objeto plano
const datos = Object.fromEntries(formData.entries());
console.log(datos);
// Iterar
for (const [clave, valor] of formData.entries()) {
console.log(clave, valor);
}
});
const inputFile = document.querySelector('input[type="file"]');
formulario.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(formulario);
// Los archivos se manejan automáticamente
const archivo = formData.get('avatar');
console.log(archivo.name); // 'foto.jpg'
console.log(archivo.size); // 123456
console.log(archivo.type); // 'image/jpeg'
// Enviar con fetch
fetch('/api/upload', {
method: 'POST',
body: formData // No pongas Content-Type, fetch lo pone solo con boundary
});
});

El navegador tiene validación nativa sin JavaScript:

<form>
<input type="email" required placeholder="Email válido">
<input type="password" minlength="8" required placeholder="Mínimo 8 caracteres">
<input type="text" pattern="[A-Za-z]+" placeholder="Solo letras">
<input type="number" min="18" max="99">
<input type="url">
<button type="submit">Enviar</button>
</form>
AtributoDescripción
requiredCampo obligatorio
minlength / maxlengthLongitud mínima/máxima de texto
min / maxValor mínimo/máximo (number, date)
patternExpresión regular que debe cumplir el valor
type="email"Valida formato de email
type="url"Valida formato de URL
formulario.setAttribute('novalidate', '');
// o
formulario.noValidate = true;

Útil cuando quieres hacer toda la validación manualmente con JS.

const campo = document.querySelector('#email');
// Verificar validez
console.log(campo.validity.valid); // true/false
console.log(campo.validity.valueMissing); // true si required y vacío
console.log(campo.validity.typeMismatch); // true si no es email válido
console.log(campo.validity.tooShort); // true si minlength no se cumple
console.log(campo.validity.patternMismatch); // true si no cumple pattern
// Mensaje de error personalizado
campo.setCustomValidity('Este campo debe ser un email válido');
// Mensaje predeterminado o personalizado
console.log(campo.validationMessage); // 'Este campo debe ser un email válido'
formulario.addEventListener('submit', (e) => {
e.preventDefault();
let valido = true;
const errores = [];
const email = formulario.elements['email'];
const password = formulario.elements['password'];
// Limpiar errores previos
document.querySelectorAll('.error').forEach(el => el.textContent = '');
// Validar email
if (!email.value.trim()) {
mostrarError(email, 'El email es obligatorio');
valido = false;
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
mostrarError(email, 'Formato de email inválido');
valido = false;
}
// Validar password
if (password.value.length < 8) {
mostrarError(password, 'La contraseña debe tener al menos 8 caracteres');
valido = false;
}
if (valido) {
enviarFormulario(new FormData(formulario));
}
});
function mostrarError(campo, mensaje) {
const error = campo.closest('.campo').querySelector('.error');
if (error) error.textContent = mensaje;
campo.classList.add('invalido');
}
const input = document.querySelector('#nombre');
// focus: el campo recibe el foco
input.addEventListener('focus', () => {
console.log('Campo enfocado');
input.style.borderColor = 'blue';
});
// blur: el campo pierde el foco
input.addEventListener('blur', () => {
console.log('Campo perdió el foco');
input.style.borderColor = '';
// Validar aquí si se quiere validación al salir
if (input.value.trim() === '') {
mostrarError(input, 'El campo no puede estar vacío');
}
});
// change: el valor cambió y perdió el foco
input.addEventListener('change', () => {
console.log('Valor final:', input.value);
});
// input: se dispara en cada cambio (cada tecla)
input.addEventListener('input', () => {
console.log('Valor actual:', input.value);
// Feedback en tiempo real
const contador = document.querySelector('#contador');
if (contador) contador.textContent = input.value.length;
});

🧠 Ejemplo completo: formulario con feedback

Section titled “🧠 Ejemplo completo: formulario con feedback”
formulario.addEventListener('input', (e) => {
const campo = e.target;
const feedback = campo.parentElement.querySelector('.feedback');
if (!feedback) return;
// Feedback en tiempo real
if (campo.validity.valid) {
feedback.textContent = '✅ Válido';
feedback.className = 'feedback valido';
} else if (campo.value.length > 0) {
feedback.textContent = campo.validationMessage || '❌ Inválido';
feedback.className = 'feedback invalido';
} else {
feedback.textContent = '';
}
});
formulario.addEventListener('submit', async (e) => {
e.preventDefault();
// Validación final
if (!formulario.checkValidity()) {
formulario.reportValidity(); // Muestra los mensajes nativos
return;
}
const formData = new FormData(formulario);
try {
const response = await fetch('/api/registro', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Error en el servidor');
const resultado = await response.json();
alert('✅ Registro exitoso');
formulario.reset();
} catch (error) {
alert(`${error.message}`);
}
});

🎯 Regla de oro: usa FormData para capturar datos, e.preventDefault() para evitar recargas, validación HTML5 como primera capa y JS para validación avanzada y feedback en tiempo real.