Saltar al contenido
formularios.js · devschool

Formularios con JavaScript

Lección 29 de 38 · 5 min de lectura · Actualizado el

En esta lección
  1. Leer el valor de un campo
  2. Todo el formulario de una vez: FormData
  3. Los eventos de un formulario
  4. Validar
  5. Al enviar

Los formularios son la forma principal en que una persona le da datos a tu aplicación. Con JavaScript puedes leer esos datos, comprobarlos mientras se escriben y enviarlos sin recargar la página.

Leer el valor de un campo

const email = document.querySelector("#email").value;          // texto
const edad = Number(document.querySelector("#edad").value);    // ¡conviértelo!
const acepta = document.querySelector("#acepto").checked;      // true / false
const ciudad = document.querySelector("#ciudad").value;        // valor del <option> elegido
  • .value siempre es texto, incluso en un <input type="number">. Convierte con Number() cuando necesites un número (y recuerda que un campo vacío se convierte en 0).
  • En casillas (checkbox) y botones de opción (radio), lo que importa es .checked.

¿Qué radio está marcado?

Los radios de un mismo grupo comparten name. Con el selector de atributo y la pseudoclase :checked obtienes el elegido:

const plan = document.querySelector("input[name=plan]:checked")?.value;

El ?. evita un error si no hay ninguno marcado. Para varias casillas marcadas:

const intereses = [...document.querySelectorAll("input[name=interes]:checked")]
  .map((casilla) => casilla.value);

Todo el formulario de una vez: FormData

En lugar de leer los campos uno a uno, FormData recoge todos los que tienen name:

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

  datos.get("email");                      // el valor de un campo
  datos.getAll("interes");                 // todos los valores de un grupo de casillas
  const objeto = Object.fromEntries(datos);   // { email: "...", plan: "pro", ... }
});

Object.fromEntries lo convierte en un objeto normal, listo para validarlo o enviarlo al servidor como JSON.

Los eventos de un formulario

EventoCuándo se disparaÚsalo para
inputCon cada cambio del valor (cada tecla, pegar, borrar…)Validar o actualizar algo en tiempo real
changeCuando el usuario termina de cambiar el valor (sale del campo, elige una opción, marca una casilla)Reaccionar a una decisión
submitAl enviar el formulario (botón o tecla Enter)Validar todo y enviar
focus / blurAl entrar / salir de un campoMostrar ayudas
resetAl vaciar el formularioRestablecer mensajes

Antiguamente se combinaban keyup, paste y mouseup para detectar cambios mientras se escribe. El evento input lo cubre todo de una vez: cualquier forma de cambiar el texto.

Recuerda que los eventos suben: si escuchas change en el formulario, te enteras de los cambios de todos sus campos con un solo manejador.

Validar

Validar es comprobar que los datos tienen sentido antes de usarlos. Primero, aprovecha lo que HTML ya hace solo (required, type="email", min, pattern…). Después, añade con JavaScript las reglas que HTML no puede expresar.

Siguiendo la idea de separar lógica e interfaz, la regla de validación va en su propia función, que no sabe nada de la página:

// Lógica: recibe un valor y devuelve un mensaje de error (o "" si está bien)
function validarUsuario(usuario) {
  if (usuario.trim().length < 3) return "El usuario debe tener al menos 3 caracteres";
  if (!/^[a-z0-9_]+$/i.test(usuario)) return "Solo letras, números y guiones bajos";
  return "";
}

// Interfaz: lee el campo, llama a la lógica y muestra el resultado
campoUsuario.addEventListener("input", () => {
  const error = validarUsuario(campoUsuario.value);
  mensajeUsuario.textContent = error;
  campoUsuario.setAttribute("aria-invalid", error ? "true" : "false");
});

/^[a-z0-9_]+$/i es una expresión regular: un patrón para comprobar textos. .test(texto) devuelve true si el texto lo cumple.

Mensajes de error accesibles

  • Pon el mensaje junto al campo, no en una alerta.
  • Relaciónalo con el campo con aria-describedby, y marca el campo con aria-invalid="true". Así los lectores de pantalla lo anuncian.
  • No uses solo el color rojo: acompáñalo de texto.
  • No valides mientras la persona aún está escribiendo por primera vez (es molesto ver “email no válido” tras la primera letra). Una buena regla: valida al salir del campo (blur o change) y, si ya había un error, vuelve a validar en cada input para que desaparezca en cuanto se corrija.
<input id="usuario" aria-describedby="error-usuario">
<p id="error-usuario" class="error" aria-live="polite"></p>

La validación del navegador desde JavaScript

Cada campo sabe si cumple sus reglas HTML:

campo.checkValidity();          // true o false
campo.validationMessage;        // el mensaje del navegador
campo.setCustomValidity("Este usuario ya existe");  // mensaje propio ("" para quitarlo)
formulario.reportValidity();    // muestra los mensajes del navegador

Si prefieres mostrar tus propios mensajes en lugar de las burbujas del navegador, añade novalidate al <form> y encárgate tú de todo en el submit.

Al enviar

formulario.addEventListener("submit", async (evento) => {
  evento.preventDefault();

  const datos = Object.fromEntries(new FormData(formulario));
  const errores = validarTodo(datos);
  if (errores.length > 0) {
    mostrarErrores(errores);
    return;
  }

  boton.disabled = true;                 // evita envíos dobles
  await enviarAlServidor(datos);         // lo verás en la lección de fetch
  boton.disabled = false;
  formulario.reset();
});

La validación en el navegador es para ayudar al usuario, no es seguridad. Cualquiera puede desactivar JavaScript o enviar datos directamente a tu servidor sin pasar por el formulario. El servidor siempre tiene que volver a comprobarlo todo.

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

formularios.html
Resultado

Pon a prueba lo que has aprendido

[JavaScript] ¿De qué tipo es campo.value en un <input type="number">?

[JavaScript] ¿Qué evento se dispara con cada tecla que cambia el contenido de un campo?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.