Saltar al contenido
formularios-validacion.html · devschool

Validar formularios en HTML

Lección 16 de 23 · 9 min de lectura · Actualizado el

En esta lección
  1. Tipos de texto
  2. Números y rangos
  3. Fechas y horas
  4. Otros tipos
  5. Validación nativa
  6. Ayudas para rellenar: autocomplete, inputmode y enterkeyhint
  7. Errores frecuentes
  8. Resumen

En la lección de formularios viste la estructura básica: <form>, <label>, name, los botones y los tipos de campo más comunes. Ahora vamos a por todos los tipos de <input> de HTML5 y a por la validación nativa: cómo conseguir que el navegador compruebe los datos antes de enviarlos, sin escribir una línea de JavaScript. Elegir bien el tipo de campo mejora mucho la experiencia en el móvil, porque cambia el teclado que aparece.

Tipos de texto

typePara quéQué aporta
textTexto corto genéricoEs el valor por defecto
emailDirecciones de correoValida el formato y muestra un teclado con @
passwordContraseñasOculta lo que se escribe
searchCajas de búsquedaMuestra una X para borrar y la tecla “Buscar”
telTeléfonosTeclado numérico de teléfono. No valida el formato
urlDirecciones webExige algo como https://...
<label for="web">Tu web o portfolio</label>
<input id="web" name="web" type="url" placeholder="https://...">

<label for="movil">Móvil</label>
<input id="movil" name="movil" type="tel" autocomplete="tel">

email admite multiple para aceptar varias direcciones separadas por comas.

Números y rangos

number

<label for="personas">Número de personas</label>
<input id="personas" name="personas" type="number" min="1" max="8" step="1" value="1">
  • min y max: valores mínimo y máximo.
  • step: de cuánto en cuánto. Con step="0.5" se admiten 1, 1.5, 2… Con step="any" se admite cualquier decimal.

Cuidado: usa number solo para cantidades. Un código postal, un DNI o un número de tarjeta no son cantidades: no se suman, pueden empezar por cero y las flechas de subir y bajar no tienen sentido. Para eso, type="text" con inputmode="numeric" (lo verás más abajo).

range

Un deslizador. No muestra el valor exacto, así que es para cuando la precisión no importa: volumen, nivel de dificultad, “¿cuánto te ha gustado?”.

<input type="range" name="dificultad" min="1" max="10" step="1" value="5">

Fechas y horas

typeFormato del valor enviadoEjemplo
dateAAAA-MM-DD2026-10-15
timeHH:MM18:30
datetime-localAAAA-MM-DDTHH:MM2026-10-15T18:30
monthAAAA-MM2026-10
weekAAAA-Www2026-W42

El navegador muestra un calendario o un selector de hora en el formato local del usuario (en España, 15/10/2026), pero envía siempre el formato de la tabla. Así el servidor no tiene que adivinar si 03/04 es 3 de abril o 4 de marzo.

<label for="dia">Día de la clase de prueba</label>
<input id="dia" name="dia" type="date" min="2026-10-01" max="2026-12-20" required>

<label for="hora">Hora</label>
<input id="hora" name="hora" type="time" min="10:00" max="21:00" step="1800">

En time, step va en segundos: 1800 son intervalos de media hora.

month y week no tienen selector en todos los navegadores (Firefox y Safari muestran un campo de texto). Úsalos con cuidado.

Otros tipos

color

Abre un selector de color y envía el valor en hexadecimal, por ejemplo #ff8800:

<label for="color-camiseta">Color de la camiseta</label>
<input id="color-camiseta" name="color" type="color" value="#1971c2">

file

Permite subir archivos. Para que funcione, el formulario debe usar method="post" y enctype="multipart/form-data":

<form action="/inscripcion" method="post" enctype="multipart/form-data">
  <label for="foto">Foto para el carné (JPG o PNG)</label>
  <input id="foto" name="foto" type="file" accept="image/png, image/jpeg">

  <label for="docs">Documentos</label>
  <input id="docs" name="docs" type="file" accept=".pdf" multiple>
</form>
  • accept filtra los archivos que se proponen: por tipo (image/*, image/png) o por extensión (.pdf). Es una ayuda, no una protección: el servidor debe comprobarlo igualmente.
  • multiple permite elegir varios archivos a la vez.

checkbox y radio

Ya los conoces. Recuerda que los radio del mismo grupo comparten name, y que ambos envían su value (si un checkbox no tiene value, envía on). Si un checkbox no está marcado, no se envía nada.

hidden

Un campo invisible que se envía con el formulario. Sirve para datos que el usuario no tiene que ver ni tocar:

<input type="hidden" name="origen" value="pagina-tarifas">

Cuidado: “oculto” no significa “secreto”. Cualquiera puede verlo y cambiarlo con las herramientas del navegador.

Validación nativa

Cuando pulsas el botón de enviar, el navegador comprueba cada campo. Si algo no cumple, no envía el formulario, pone el foco en el primer campo con error y muestra un mensaje.

Los atributos de validación

AtributoSe aplica aComprueba
requiredCasi todosQue no esté vacío (en checkbox, que esté marcado)
minlength / maxlengthTextoNúmero de caracteres
min / maxNúmeros, fechas, horas, rangeValor mínimo y máximo
stepNúmeros, fechas, horasQue el valor encaje en los saltos
patternTexto, tel, email, url, search, passwordQue cumpla una expresión regular
typeemail, urlQue tenga el formato correcto

maxlength es especial: no muestra error, directamente impide escribir más caracteres.

pattern con ejemplos

pattern usa una expresión regular (lo verás a fondo en expresiones regulares). Debe coincidir con todo el valor, no con una parte:

<!-- Código postal español: 5 cifras -->
<input name="cp" pattern="[0-9]{5}" title="5 cifras, por ejemplo 39005">

<!-- Móvil español: empieza por 6 o 7 y tiene 9 cifras -->
<input name="movil" type="tel" pattern="[67][0-9]{8}">

<!-- Usuario: letras minúsculas, números y guion bajo, de 3 a 15 caracteres -->
<input name="usuario" pattern="[a-z0-9_]{3,15}">

El title se añade al mensaje de error en muchos navegadores. Explica ahí el formato esperado.

Un campo vacío sin required se considera válido aunque tenga pattern: el patrón solo se comprueba si hay algo escrito.

Desactivar la validación: novalidate y formnovalidate

<form action="/perfil" method="post" novalidate>
  ...
</form>

novalidate en el <form> desactiva todas las comprobaciones. Se usa cuando validas con JavaScript y quieres mostrar tus propios mensajes.

formnovalidate va en un botón concreto. Es útil para un “Guardar borrador” que debe funcionar aunque falten campos obligatorios:

<button type="submit">Publicar</button>
<button type="submit" formnovalidate name="borrador" value="1">Guardar borrador</button>

Dar estilo según el estado: :valid, :invalid y :user-invalid

CSS tiene pseudoclases que se activan según la validez del campo:

input:invalid { border-color: #c92a2a; }
input:valid { border-color: #2f9e44; }
input:user-invalid { background: #fff5f5; }

El problema de :invalid es que se activa nada más cargar la página: un campo required vacío ya está en rojo antes de que el usuario haga nada. :user-invalid solo se activa después de que el usuario haya interactuado con el campo (o intentado enviar), que es lo que suele quererse.

Recuerda: la validación del navegador es comodidad para el usuario, no seguridad. Cualquiera puede quitar un required con F12. El servidor debe validar siempre.

Ayudas para rellenar: autocomplete, inputmode y enterkeyhint

autocomplete

Le dice al navegador qué dato va en el campo, para que lo rellene con lo que tiene guardado. Ahorra mucho tiempo, sobre todo en el móvil:

ValorDato
name, given-name, family-nameNombre completo, nombre, apellidos
email, telCorreo y teléfono
street-address, postal-code, address-level2, country-nameDirección, código postal, ciudad, país
usernameNombre de usuario
current-passwordContraseña al iniciar sesión
new-passwordContraseña nueva (el navegador puede sugerir una segura)
one-time-codeCódigo de verificación de un SMS
bdayFecha de nacimiento
offNo autocompletar
<input name="usuario" autocomplete="username">
<input name="clave" type="password" autocomplete="current-password">

inputmode

Elige el teclado del móvil sin cambiar el tipo de campo ni añadir validación:

ValorTeclado
numericSolo cifras
decimalCifras y separador decimal
telTeclado de teléfono
emailCon @ y .
urlCon / y .com
searchCon tecla de buscar
noneNinguno (para teclados propios)
<input name="cp" inputmode="numeric" pattern="[0-9]{5}" autocomplete="postal-code">

Esta es la forma correcta de pedir un código postal: texto, teclado numérico y patrón.

enterkeyhint

Cambia el texto de la tecla Intro del teclado del móvil: enter, done, go, next, previous, search o send.

<input name="mensaje" enterkeyhint="send">

Errores frecuentes

  • type="number" para códigos o DNI. Pierde los ceros iniciales y muestra flechas absurdas. Usa texto con inputmode="numeric".
  • Olvidar enctype="multipart/form-data" al subir archivos: solo se envía el nombre del archivo.
  • pattern con ^ y $ pensando que hacen falta. El navegador ya exige que coincida el valor entero.
  • Confiar en accept o en required como seguridad. Se saltan fácilmente.
  • Estilos con :invalid que ponen todo en rojo antes de empezar. Usa :user-invalid.
  • Poner autocomplete="off" en todo. Fastidia a los usuarios y los gestores de contraseñas lo ignoran.

Resumen

Quiero…Uso
Un email, una web, un teléfonotype="email", type="url", type="tel"
Una cantidadtype="number" con min, max, step
Un deslizadortype="range"
Una fecha u horadate, time, datetime-local, month, week
Subir archivostype="file" + accept + multiple + enctype
Campo obligatoriorequired
Formato concretopattern + title
Longitud de textominlength, maxlength
Rellenar automáticamenteautocomplete con el valor adecuado
Otro teclado en el móvilinputmode y enterkeyhint
Validar con JavaScriptnovalidate en el form

Pruébalo tú

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

formularios-validacion.html
Resultado

Pon a prueba lo que has aprendido

[HTML] ¿Qué valores acepta este campo sin mostrar error al enviar?
<input type="number" name="personas" min="1" max="8" step="1" required>

[HTML] ¿Cuál es el problema de este campo?
<input type="text" name="cp" pattern="[0-9]{5}">

[HTML] ¿Para qué sirve inputmode="numeric"?

[HTML] Un formulario tiene dos botones: Enviar y Guardar borrador. ¿Cómo dejas que el borrador se guarde sin pasar la validación?

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