Más campos de formulario en HTML
En esta lección
- Todos los tipos de <input> de un vistazo
- <select> a fondo
- Sugerencias con <datalist>
- <textarea> en detalle
- Agrupar y desactivar: <fieldset disabled>
- Mostrar resultados con <output>
- Varios botones en un formulario
- Campos fuera del <form>: el atributo form
- Subir archivos
- Ayudas para escribir en el móvil
- Qué envía realmente un formulario
- Errores frecuentes
- Resumen
Ya sabes montar un formulario con <form>, <label>, <input> y botones, y en validar formularios verás cómo hacer que el navegador compruebe los datos. Esta lección recoge todo lo demás: los detalles de <select> y <textarea>, las sugerencias con <datalist>, el elemento <output>, formularios con varios botones que hacen cosas distintas, campos que viven fuera del <form>, la subida de archivos y unos cuantos atributos que mejoran mucho la experiencia en el móvil. Al final sabrás exactamente qué datos envía un formulario y en qué forma.
Todos los tipos de <input> de un vistazo
El atributo type cambia por completo el comportamiento de un <input>. Esta es la lista completa, para que la tengas a mano:
| Grupo | Tipos | Dónde se explican |
|---|---|---|
| Texto | text, search, email, url, tel, password | Validar formularios |
| Números | number, range | Validar formularios |
| Fechas y horas | date, time, datetime-local, month, week | Validar formularios |
| Elegir | checkbox, radio, color | Formularios y aquí |
| Archivos | file | Aquí, más abajo |
| Ocultos | hidden | Validar formularios |
| Botones | submit, reset, button, image | Aquí, más abajo |
Si escribes un type que no existe (o que un navegador muy antiguo no conoce), el campo se comporta como text. Por eso los tipos modernos se pueden usar sin miedo: en el peor caso, el usuario ve una caja de texto normal.
<select> a fondo
Grupos de opciones: <optgroup>
En una lista larga, agrupa las opciones con <optgroup>. Su label aparece como un título que no se puede elegir:
<label for="ruta">Ruta</label>
<select id="ruta" name="ruta">
<optgroup label="Fácil">
<option value="senda-costera">Senda costera (8 km)</option>
<option value="bosque-hayas">Bosque de hayas (6 km)</option>
</optgroup>
<optgroup label="Difícil">
<option value="pico-cueto">Pico del Cueto (14 km)</option>
</optgroup>
</select>
Los grupos no se pueden anidar: un <optgroup> no puede contener otro.
value, selected y disabled
- Lo que se envía es el
valuede la opción. El texto entre las etiquetas es lo que ve el usuario. Si no ponesvalue, se envía el texto. selectedmarca la opción elegida al cargar la página. Sin él, se elige la primera.disableden una<option>la muestra en gris y no se puede elegir (útil para “completo” o “agotado”). En un<optgroup>desactiva todo el grupo.
Una opción vacía para obligar a elegir
Si el <select> tiene required y la primera opción tiene value="", el navegador no deja enviar hasta que el usuario elija otra:
<select name="talla" required>
<option value="">Elige tu talla</option>
<option value="s">S</option>
<option value="m">M</option>
<option value="l">L</option>
</select>
Así evitas que se envíe la primera talla de la lista solo porque nadie la cambió.
Elegir varias: multiple y size
Con multiple, el usuario puede marcar varias opciones (con Ctrl o Cmd + clic). size indica cuántas filas se ven a la vez:
<label for="dias">Días que puedes venir</label>
<select id="dias" name="dias" multiple size="4">
<option value="lun">Lunes</option>
<option value="mar">Martes</option>
<option value="mie">Miércoles</option>
<option value="jue">Jueves</option>
</select>
Mucha gente no sabe usar un <select multiple> en el ordenador. Si son pocas opciones, un grupo de casillas (checkbox) suele ser más claro.
Sugerencias con <datalist>
En formularios viste que <datalist> propone valores en un campo de texto, pero deja escribir otros. Se conecta con el atributo list, que debe coincidir con el id del <datalist>. Además de texto, funciona con otros tipos:
<!-- Marcas en un deslizador -->
<label for="volumen">Volumen</label>
<input id="volumen" name="volumen" type="range" min="0" max="100" list="marcas">
<datalist id="marcas">
<option value="0"></option>
<option value="50"></option>
<option value="100"></option>
</datalist>
<!-- Colores sugeridos -->
<label for="color">Color del dorsal</label>
<input id="color" name="color" type="color" list="colores">
<datalist id="colores">
<option value="#e03131"></option>
<option value="#1971c2"></option>
<option value="#2f9e44"></option>
</datalist>
Cuándo elegir cada uno:
| Necesito… | Uso |
|---|---|
| Que solo se pueda elegir de una lista cerrada | <select> |
| Proponer valores, pero aceptar cualquier otro | <input list> + <datalist> |
| Pocas opciones que se vean todas a la vez | radio o checkbox |
Cuidado: el aspecto de las sugerencias de
<datalist>lo decide cada navegador y no se puede cambiar con CSS. Tampoco es una validación: el usuario puede escribir lo que quiera.
<textarea> en detalle
<label for="comentario">Cuéntanos tu experiencia</label>
<textarea id="comentario" name="comentario" rows="5" maxlength="500"
placeholder="Qué te gustó, qué mejorarías..."></textarea>
rowses el número de líneas visibles;cols, la anchura en caracteres. Hoy la anchura se suele dar con CSS (width: 100%).maxlengthyminlengthfuncionan igual que en<input>.- No tiene atributo
value. El texto inicial va entre las etiquetas, y se respeta tal cual, con sus espacios y saltos de línea. Por eso<textarea>y</textarea>se escriben pegados cuando debe empezar vacío: cualquier espacio entre ellos sería contenido. - Con CSS,
resize: verticaldeja que el usuario lo agrande solo en altura, yfield-sizing: content(de momento, solo en algunos navegadores) hace que crezca solo según lo que se escribe.
Agrupar y desactivar: <fieldset disabled>
<fieldset> y <legend> ya los conoces de formularios. Un detalle útil: disabled en un <fieldset> desactiva todos los campos de dentro de golpe, y ninguno se envía:
<label><input type="checkbox" id="factura"> Quiero factura</label>
<fieldset id="datos-factura" disabled>
<legend>Datos de facturación</legend>
<label for="nif">NIF</label>
<input id="nif" name="nif">
<label for="razon">Razón social</label>
<input id="razon" name="razon">
</fieldset>
Con una línea de JavaScript activas el grupo cuando se marca la casilla:
const casilla = document.querySelector('#factura');
const grupo = document.querySelector('#datos-factura');
casilla.addEventListener('change', () => {
grupo.disabled = !casilla.checked;
});
Mostrar resultados con <output>
<output> contiene el resultado de un cálculo hecho con otros campos: un total, una media, un precio final. El atributo for lista los id de los campos de los que depende, separados por espacios:
<form id="calculo">
<label for="sesiones">Sesiones</label>
<input id="sesiones" type="number" min="1" value="4">
<label for="precio">Precio por sesión</label>
<input id="precio" type="number" min="0" value="12">
<p>Total: <output id="total" for="sesiones precio">48</output> €</p>
</form>
const formulario = document.querySelector('#calculo');
formulario.addEventListener('input', () => {
const sesiones = Number(formulario.querySelector('#sesiones').value);
const precio = Number(formulario.querySelector('#precio').value);
formulario.querySelector('#total').value = sesiones * precio;
});
El evento input del formulario se dispara cada vez que cambia cualquiera de sus campos, así que no hace falta escuchar uno por uno. Los lectores de pantalla anuncian los cambios de un <output> sin que tengas que hacer nada más.
Nota: un
<output>no se envía con el formulario, aunque tenganame. Si el servidor necesita el total, debe calcularlo él (y debería hacerlo siempre: nunca te fíes de un precio que llega del navegador).
Sus parientes <progress> (avance de una tarea) y <meter> (una medida dentro de un rango) se explican en details y dialog.
Varios botones en un formulario
Saber qué botón se pulsó: name y value
Un botón de envío puede llevar name y value. Solo se envía el del botón que se ha pulsado:
<form action="/comentario" method="post">
<label for="texto">Comentario</label>
<textarea id="texto" name="texto"></textarea>
<button type="submit" name="accion" value="publicar">Publicar</button>
<button type="submit" name="accion" value="borrador">Guardar borrador</button>
</form>
El servidor recibe accion=publicar o accion=borrador y actúa en consecuencia.
Cambiar el destino: formaction y compañía
Los botones de envío pueden sobrescribir los atributos del formulario solo cuando se pulsan ellos:
| Atributo del botón | Sustituye a… del <form> |
|---|---|
formaction | action: a qué dirección se envía |
formmethod | method: get o post |
formenctype | enctype: cómo se codifican los datos |
formtarget | target: dónde se abre la respuesta |
formnovalidate | novalidate: enviar sin validar |
<form action="/pedido" method="post">
...
<button type="submit">Confirmar pedido</button>
<button type="submit" formaction="/presupuesto" formtarget="_blank" formnovalidate>
Ver presupuesto en PDF
</button>
</form>
El ejemplo del editor usa esta idea: prueba los dos botones y mira a qué dirección iría cada envío.
<input type="image"> y reset
type="image"es un botón de envío con forma de imagen (srcyaltobligatorios). Hoy es más flexible un<button>con una imagen o un icono dentro.type="reset"vacía el formulario. Casi nunca es buena idea: es fácil pulsarlo sin querer y perder todo.
Campos fuera del <form>: el atributo form
A veces el diseño obliga a colocar un campo o un botón fuera del <form>, por ejemplo en una barra fija abajo. El atributo form lo asocia por id:
<form id="buscador" action="/buscar" method="get">
<label for="q">Buscar</label>
<input id="q" name="q" type="search">
</form>
<!-- En otra parte de la página -->
<button type="submit" form="buscador">Buscar</button>
El botón envía el formulario buscador aunque no esté dentro de él. Funciona en <input>, <select>, <textarea>, <button>, <output> y <fieldset>.
El elemento <search>
Para marcar una zona de búsqueda o de filtros, HTML tiene el elemento <search>. Envuelve al formulario y le dice a los lectores de pantalla que ahí se busca:
<search>
<form action="/buscar" method="get">
<label for="busqueda">Buscar curso</label>
<input id="busqueda" name="q" type="search">
<button>Buscar</button>
</form>
</search>
Subir archivos
Lo básico (type="file", accept, multiple y enctype="multipart/form-data") lo tienes en validar formularios. Algunos detalles más:
<form action="/subir" method="post" enctype="multipart/form-data">
<label for="fotos">Fotos de la ruta (máximo 5)</label>
<input id="fotos" name="fotos" type="file" accept="image/*" multiple>
<label for="selfi">Foto para el carné</label>
<input id="selfi" name="selfi" type="file" accept="image/*" capture="user">
<button>Subir</button>
</form>
accept="image/*"acepta cualquier imagen; también puedes combinar tipos y extensiones:accept=".pdf,.docx".capturepide al móvil que abra directamente la cámara:useres la frontal yenvironment, la trasera. En el ordenador se ignora.- HTML no puede limitar el número de archivos ni su tamaño. Eso se comprueba con JavaScript (leyendo
input.files) y, sobre todo, en el servidor. Lo verás en ficheros y subidas con PHP. - Por seguridad, un
<input type="file">no admite unvalueinicial: el usuario siempre tiene que elegir el archivo.
Ayudas para escribir en el móvil
Además de autocomplete, inputmode y enterkeyhint (explicados en validar formularios), hay tres atributos más que evitan pequeñas molestias:
| Atributo | Valores | Para qué |
|---|---|---|
autocapitalize | off, sentences, words, characters | Evita que el móvil ponga la primera letra en mayúscula en un usuario o un código |
spellcheck | true, false | Quita el subrayado rojo en campos donde no tiene sentido (códigos, nombres de usuario) |
autofocus | (booleano) | Pone el cursor en ese campo al cargar. Úsalo solo en páginas cuyo único fin es ese formulario, como un buscador |
<label for="usuario">Usuario</label>
<input id="usuario" name="usuario" autocomplete="username"
autocapitalize="off" spellcheck="false">
En formularios de compra, autocomplete admite además prefijos para separar direcciones: shipping street-address (dirección de envío) y billing street-address (dirección de facturación). Así el navegador no mezcla las dos.
Qué envía realmente un formulario
Entender esto te ahorrará muchos errores cuando programes el servidor. Al enviar:
- Solo se envían los campos con
name. - Los campos
disabled(o dentro de un<fieldset disabled>) no se envían. Losreadonly, sí. - Las casillas y los radios sin marcar no envían nada: ni siquiera su nombre.
- Varias casillas con el mismo
name, o un<select multiple>, envían el nombre repetido, una vez por cada valor. - Los
<output>no se envían. - Los espacios y caracteres especiales se codifican: un espacio se convierte en
+y laóen%C3%B3.
Por ejemplo, con dos casillas intereses marcadas (bloque y yoga) y el campo cupon asociado con form, un envío con method="get" produce esta consulta en la URL:
?intereses=bloque&intereses=yoga&cupon=VERANO
Consejo: en PHP, para recibir varios valores con el mismo nombre como un array, el
namedebe terminar en corchetes:name="intereses[]". Otros lenguajes de servidor leen todos los valores sin necesidad de corchetes.
Errores frecuentes
<select required>cuya primera opción tiene valor. La validación nunca falla, porque ya hay algo elegido. La opción de “Elige una” debe tenervalue="".- Escribir el texto inicial de un
<textarea>envalue. No existe; va entre las etiquetas. - Dejar espacios entre
<textarea>y</textarea>. El campo aparece con espacios en blanco dentro. - Esperar que un
<output>llegue al servidor. No se envía; calcula el resultado en el servidor. - Botones sin
typeque no deberían enviar. Un<button>dentro de un formulario envía por defecto. Si es para otra cosa,type="button". - Confiar en
acceptpara filtrar archivos peligrosos. Es solo una ayuda para el usuario; el servidor debe revisar tipo y tamaño. - Desactivar con
disabledun dato que el servidor necesita. No se enviará. Si solo quieres que no se edite, usareadonly.
Resumen
| Quiero… | Uso |
|---|---|
| Agrupar opciones de un desplegable | <optgroup label="..."> |
Obligar a elegir en un <select> | required + primera opción con value="" |
| Elegir varias opciones | <select multiple size="..."> o casillas |
| Sugerir valores sin cerrar la lista | <input list> + <datalist> |
| Texto largo con valor inicial | <textarea>texto</textarea> |
| Desactivar un grupo de campos | <fieldset disabled> |
| Mostrar un resultado calculado | <output for="..."> |
| Saber qué botón se pulsó | name y value en el botón |
| Que un botón envíe a otra dirección | formaction, formmethod, formtarget |
| Un campo fuera del formulario | Atributo form="id-del-form" |
| Abrir la cámara del móvil | type="file" + accept="image/*" + capture |
| Evitar mayúsculas automáticas | autocapitalize="off" |
En la siguiente lección aprenderás a comprobar los datos antes de enviarlos: validar formularios.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.