Trabajar con formularios en JavaScript te permite capturar lo que escribe un usuario, evitar la recarga del navegador y guardar esa información directamente en el navegador para reutilizarla. Si ya manejas el DOM, este es el siguiente paso natural para construir interfaces interactivas reales.
Por qué conviene separar proyectos antes de tocar código nuevo
Antes de escribir lógica nueva, crea una carpeta dedicada (por ejemplo, Form) con sus propios index.html y app.js. Copia el contenido de tu proyecto anterior del DOM, pero trabaja sobre los archivos nuevos. Así proteges tu historial de aprendizaje y evitas errores invisibles que aparecen cuando copias y pegas sin notarlo.
¿Por qué copiar y pegar genera errores? Porque arrastras referencias, IDs o estilos del proyecto original que no aplican al nuevo contexto, y luego es muy difícil rastrear de dónde viene el fallo.
Dentro del main agregas una nueva sección con el formulario de contacto: un form con ID contact-form, un input para el nombre, un textarea para el mensaje y un botón type="submit". Los estilos se enlazan al final del bloque styles para mantener el orden. [04:30]
Cómo evitar la recarga del formulario con preventDefault
Cuando envías un formulario sin JavaScript, el navegador recarga la página y manda los valores como parámetros en la URL. Para frenar ese comportamiento usas el método preventDefault del evento.
La función handleContactSubmit recibe el parámetro event. Sin ese parámetro, no existe el objeto del evento dentro de la función, así que pasarlo es obligatorio. Una vez dentro, llamas a event.preventDefault() para que el formulario deje de recargar la página y puedas seguir trabajando con los datos. [08:10]
¿Qué hace event.preventDefault en un formulario? Cancela la acción por defecto del navegador, que en un submit significa recargar la página. Sin esto, pierdes la información antes de procesarla.
Cómo extraer datos con event.target y la API FormData
Para capturar lo que escribió el usuario, primero obtienes el formulario desde event.target y luego construyes una instancia de FormData pasándole ese formulario.
js
const form = event.target;
const formData = new FormData(form);
const name = String(formData.get("name"));
const message = String(formData.get("message"));
El método formData.get("name") recupera el valor asociado al atributo name de cada campo del HTML. Por eso es crítico que en el input y en el textarea el atributo name coincida exactamente con la llave que pides en JavaScript: si el HTML dice name="message", tu get también debe usar "message". [11:45]
Cómo construir el payload con shorthand de objetos
Una vez tienes los valores, los empaquetas en un objeto junto con la fecha:
js
const payload = {
name,
message,
date: new Date().toISOString()
};
Cuando la llave y la variable se llaman igual, JavaScript permite omitir la repetición. En lugar de name: name, escribes solo name. El método toISOString() genera un timestamp estandarizado que facilita ordenar o comparar mensajes después.
Cómo guardar información en el navegador con localStorage
El objeto localStorage permite almacenar pares de llave y valor que persisten en el navegador, pero solo dentro de la misma URL. No viaja a otros sitios ni a otros dominios.
Para guardar el payload usas:
js
localStorage.setItem(CONTACT_STORAGE_KEY, JSON.stringify(payload));
form.reset();
La constante CONTACT_STORAGE_KEY se define al inicio del archivo en snake case o como identificador descriptivo. Es una buena práctica porque hace tu código más legible que escribir "form" suelto en cada llamada. El método JSON.stringify convierte el objeto a string, ya que localStorage solo acepta texto. Y form.reset() limpia los campos después de enviar. [16:20]
¿Qué es localStorage y para qué sirve? Es una API del navegador que guarda datos como llave-valor en formato string, accesibles mientras estés en la misma página. Sirve para preferencias, borradores y estados que deben sobrevivir a recargas.
Cómo registrar el listener del submit
El formulario solo responde si lo conectas con addEventListener:
js
const contactForm = document.querySelector("#contact-form");
if (contactForm) {
contactForm.addEventListener("submit", handleContactSubmit);
}
La validación con if evita errores si el elemento no existe en esa página. Escuchas el evento submit, no el click del botón, porque submit también se dispara cuando el usuario presiona Enter dentro del campo.
Cómo mostrar los mensajes guardados en la interfaz
Guardar los datos no basta: el usuario quiere verlos. Para eso creas la función renderSavedMessage, que lee el localStorage, parsea el string a objeto y construye el HTML.
js
function renderSavedMessage() {
const box = document.querySelector("#last-saved-message");
const raw = localStorage.getItem(CONTACT_STORAGE_KEY);
if (!raw) return;
const data = JSON.parse(raw);
box.classList.remove("hidden");
box.innerHTML = <strong>Último mensaje guardado</strong> <p><strong>Nombre:</strong> ${data.name}</p> <p><strong>Mensaje:</strong> ${data.message}</p> ;
}
Aquí pasan tres cosas clave: JSON.parse reconstruye el objeto desde el string, classList.remove("hidden") revela el contenedor que estaba oculto por CSS, y los template strings con backticks permiten inyectar variables con ${} directamente dentro del HTML. [22:05]
Ejecutas renderSavedMessage() en dos momentos: al final de handleContactSubmit para reflejar el último envío, y al cargar la página por primera vez para recuperar mensajes previos.
Cómo verificar el almacenamiento desde las DevTools
Abre las DevTools del navegador, ve a la pestaña Application y busca Storage. Ahí encuentras localStorage, sessionStorage, cookies y bases de datos pequeñas como IndexedDB. Al hacer clic en tu dominio verás la llave que definiste y el JSON con name, message y date. Cada nuevo envío sobrescribe el valor anterior porque estás usando la misma llave.
Desde aquí puedes escalar el ejercicio: validar que el mensaje tenga al menos 10 caracteres, guardar un arreglo de mensajes en lugar de uno solo, o enviar el payload a una API real con fetch. ¿Cómo resolverías tú la validación del formulario? Cuéntalo en los comentarios.