Skip to content
This page has been auto-translated and may contain errors.View in English

Formularios y campos de entrada ​

docs.scrimba.com

Casi todo sitio web que hace algo por ti, un buscador, un inicio de sesión, un checkout, un campo de comentarios, está construido sobre un formulario. Un formulario es lo que permite que una página deje de solo hablarle al visitante y empiece a escucharlo: recolecta lo que alguien escribe, marca o selecciona, y entrega esos datos para que se actúe sobre ellos. Este capítulo trata sobre el HTML que hace esto posible.

El elemento form ​

Un formulario es un contenedor que agrupa las cosas que un visitante llena. Envuelves tus campos de entrada y un botón dentro de un elemento <form>, y el trabajo del formulario es juntar todo y enviarlo cuando el visitante termina.

html
<form>
  <label>Tu nombre</label>
  <input>
  <button>Enviar</button>
</form>

Piénsalo como un formulario de papel que llenas en el consultorio de un médico. La hoja contiene todas las casillas, tú escribes en cada una, y al final entregas toda la hoja de una vez. El elemento <form> es esa hoja.

Un <form> es el contenedor que recolecta sus controles y los envía como un solo paquete. Dos atributos definen lo que pasa al enviar: action es la dirección a la que se envían los datos, y method es cómo se envían.

html
<form action="/subscribe" method="post">
  <label for="email">Correo electrónico</label>
  <input id="email" name="email" type="email">
  <button type="submit">Suscribirse</button>
</form>

Cuando el visitante activa el envío, el navegador recolecta cada control con nombre dentro del formulario, empaqueta los valores, y envía una solicitud a la dirección de action. No tienes que programar nada de esto tú mismo. El elemento <form> te da este comportamiento gratis, y por eso agrupar controles en un formulario real es mejor que dispersar campos de entrada sueltos por la página.

Un <form> es un mecanismo nativo de envío: el comportamiento incorporado del navegador que junta los controles del formulario en una solicitud y la envía, sin necesidad de scripts. Al enviar, el navegador lee el action (la URL a la que va la solicitud) y el method (get o post, que se explican más adelante en este capítulo), serializa los controles dentro de la solicitud, y navega a la nueva página. Ese comportamiento por defecto es la razón por la que un formulario sigue funcionando con JavaScript deshabilitado o aún no cargado, y es la base sobre la que construyes, no algo que reemplazas.

html
<form action="/subscribe" method="post">
  <label for="email">Correo electrónico</label>
  <input id="email" name="email" type="email">
  <button type="submit">Suscribirse</button>
</form>

Hay dos comportamientos que suelen sorprender. Primero, un formulario se envía al presionar Enter cuando el foco está en un campo de texto de una sola línea, no solo al hacer clic en un botón, así que el envío por teclado viene incluido y conviene probarlo. Segundo, el envío por defecto recarga o navega a otra página; cuando manejas un formulario con JavaScript, llamas a event.preventDefault() para detener esa navegación y tomar el control. Trata el envío nativo como el piso mínimo: debería hacer algo razonable incluso antes de que corra cualquier script. El mejoramiento progresivo, agregar scripts encima de HTML que ya funciona, es la razón para empezar así.

JunoEl elemento form Un <form> es el contenedor que guarda tus campos de entrada y un botón, y envía todo junto cuando alguien lo manda. Imagina el formulario de papel en un escritorio: una sola hoja, muchas casillas, entregada toda de una vez. Envuelve tus campos en un formulario y obtienes ese comportamiento de juntar-y-enviar sin hacer nada complicado.
JunoEl elemento form El <form> junta sus controles y los envía; action dice a dónde, method dice cómo. Obtienes la recolección y el envío gratis, así que conviene usar un formulario real en lugar de campos sueltos más tu propio manejador de clics. Te ahorra código y funciona antes de que cargue cualquier script.
JunoEl elemento form El envío nativo junta los controles y manda una solicitud a action sin ningún script involucrado, y por eso un formulario sigue funcionando cuando JavaScript está desactivado. Recuerda que se envía al presionar Enter en un campo de texto, no solo con un clic. Cuando tomas el control con JavaScript, event.preventDefault() detiene la navegación por defecto, y el formulario simple que hay debajo sigue siendo tu red de seguridad.

Campos de entrada y etiquetas ​

Un <input> es la caja en la que un visitante escribe. Por sí solo, sin embargo, un input es una caja vacía sin ninguna pista de para qué sirve. Ahí es donde entra una etiqueta: es el texto que le dice al visitante qué poner en la caja.

html
<label for="city">Ciudad</label>
<input id="city">

Todo input necesita una etiqueta. Conectas los dos dándole un id al input y apuntando la etiqueta hacia él con for, usando la misma palabra en ambos. Una vez conectados, hacer clic en la etiqueta pone el cursor en la caja, lo cual es un pequeño detalle que hace el formulario más fácil de usar para todos.

Un <input> recolecta un solo valor; un <label> le pone nombre. Los asocias haciendo coincidir el atributo for de la etiqueta con el id del input:

html
<label for="username">Nombre de usuario</label>
<input id="username" name="username" type="text" required>

Hay algunos atributos que se ganan su lugar en la mayoría de los inputs. name es la clave bajo la cual se envía el valor (esto se explica en la sección sobre nombres). required bloquea el envío hasta que se llene el campo. placeholder muestra un texto de ayuda tenue dentro de la caja, pero un placeholder no es una etiqueta: desaparece en el momento en que alguien empieza a escribir, así que no puede reemplazar a la etiqueta real. Todo input sigue necesitando su propio <label>.

Hay dos maneras de asociar un <label> con su control, y la distinción importa para la accesibilidad. La primera es la asociación explícita: la etiqueta lleva un atributo for cuyo valor es igual al id del control. La segunda es la asociación implícita: envuelves el control dentro del elemento label, sin necesidad de for ni id.

html
<!-- explícita: for coincide con id -->
<label for="phone">Teléfono</label>
<input id="phone" name="phone" type="tel">

<!-- implícita: el input está dentro del label -->
<label>
  Teléfono
  <input name="phone" type="tel">
</label>

Ambas le dan al control un nombre accesible, el texto que un lector de pantalla anuncia cuando el campo recibe el foco, de modo que un visitante que no puede ver el diseño de la página igual sepa qué escribir. Prefiere la asociación explícita: sobrevive a diseños CSS que alejan la etiqueta del input, y funciona incluso cuando el estilo obliga a separarlos. Un campo sin etiqueta asociada se anuncia como una caja de edición vacía sin propósito, lo cual es uno de los fallos de accesibilidad más comunes en la web. El capítulo de accesibilidad profundiza más en esto.

JunoCampos de entrada y etiquetas El <input> es la caja donde la gente escribe, y el <label> es el texto que les dice qué va ahí. Enlázalos con un for y un id que coincidan, y al hacer clic en la etiqueta saltas directo a la caja. Dale una etiqueta a cada input; una caja solitaria sin palabras al lado solo confunde a la gente.
JunoCampos de entrada y etiquetas Haz coincidir el for de la etiqueta con el id del input y quedan conectados. Apóyate en required para bloquear envíos vacíos, pero no dejes que un placeholder haga las veces de etiqueta: desaparece en el instante en que alguien empieza a escribir. Etiqueta real, en cada campo, sin excepciones.
JunoCampos de entrada y etiquetas Dos formas de enlazar una etiqueta: for coincidiendo con id, o envolviendo el input dentro de la etiqueta. Ambas le dan al campo un nombre accesible, el texto que lee un lector de pantalla, y el for/id explícito es la opción más sólida cuando el CSS separa las cosas. Un campo sin etiqueta se lee como una caja sin nombre, uno de los descuidos de accesibilidad más comunes que existen.

Otros controles ​

No todo es una simple caja de texto. Algunas preguntas se responden mejor marcando, eligiendo, o seleccionando de una lista. Una casilla de marcar (un checkbox) es un interruptor de encendido o apagado, y un menú desplegable deja que alguien elija una opción entre varias.

html
<label>
  <input type="checkbox"> Enviarme el boletín
</label>

<label for="size">Talla</label>
<select id="size">
  <option>Pequeña</option>
  <option>Mediana</option>
  <option>Grande</option>
</select>

No tienes que memorizar esto. Lo importante por ahora es que un formulario puede hacer sus preguntas de la manera que mejor le convenga: escribiéndola, marcándola, o eligiéndola de una lista.

El elemento <input> cambia de forma según su atributo type, y el tipo correcto te da un mejor teclado, validación incorporada, y un selector nativo gratis:

html
<input type="email">     <!-- valida la forma del @, teclado de correo en el celular -->
<input type="number">    <!-- teclado numérico, flechas para subir/bajar -->
<input type="password">  <!-- oculta los caracteres -->
<input type="date">      <!-- selector de fecha nativo -->
<input type="checkbox">  <!-- un interruptor simple de encendido/apagado -->
<input type="radio">     <!-- elegir una opción entre un grupo -->

Más allá de <input>, hay tres elementos que cubren el resto. <textarea> es una caja de texto de varias líneas para escritos más largos. <select> con hijos <option> es un menú desplegable. Y cuando necesitas agrupar campos relacionados de forma visual y semántica, los envuelves en un <fieldset> con un <legend> que nombra al grupo:

html
<fieldset>
  <legend>Velocidad de entrega</legend>
  <label><input type="radio" name="speed" value="standard"> Estándar</label>
  <label><input type="radio" name="speed" value="express"> Exprés</label>
</fieldset>

<label for="message">Mensaje</label>
<textarea id="message" name="message" rows="4"></textarea>

Los botones también llevan un type. type="submit" envía el formulario (es el valor por defecto de un botón dentro de un formulario), type="reset" restablece todos los campos a su valor inicial, y type="button" no hace nada por sí solo y está ahí para que JavaScript se conecte a él.

Cada tipo de control trae comportamientos que conviene conocer antes de publicarlo. Los botones de radio se agrupan por un name compartido: solo se puede seleccionar un radio por grupo a la vez, y esa agrupación es lo que los hace mutuamente excluyentes, así que un name faltante o que no coincide rompe silenciosamente el grupo en interruptores independientes.

html
<fieldset>
  <legend>Velocidad de entrega</legend>
  <label><input type="radio" name="speed" value="standard" checked> Estándar</label>
  <label><input type="radio" name="speed" value="express"> Exprés</label>
</fieldset>

Un <fieldset> y un <legend> no son solo visuales. El <legend> es anunciado por los lectores de pantalla como contexto para cada control del grupo, de modo que un radio dentro del fieldset "Velocidad de entrega" se lee como "Velocidad de entrega, Estándar", que es la diferencia entre un formulario comprensible y una lista de opciones sin contexto. Dos notas más para producción. Un checkbox solo envía su valor cuando está marcado; una casilla sin marcar está completamente ausente del envío, así que el servidor no ve nada en lugar de un false, lo cual determina cómo lees los datos del otro lado. Y type="submit" es el tipo por defecto de un <button> dentro de un formulario, así que un <button> sin especificar que pensabas usar como disparador de JavaScript va a enviar el formulario y recargar la página a menos que fijes type="button". Ese comportamiento por defecto sorprende a mucha gente constantemente.

JunoOtros controles Un formulario puede preguntar de más de una manera: una casilla para sí-o-no, un menú desplegable para elegir una cosa de una lista. Un checkbox es <input type="checkbox">, y un menú desplegable es un <select> con opciones <option> dentro. No hace falta memorizarlos; el formulario tiene más que simples cajas cuando una pregunta lo necesita.
JunoOtros controles El type del input le cambia la forma: email, number, date, password, checkbox, radio, cada uno con un teclado o selector apropiado. Usa <textarea> para texto largo, <select> para un menú desplegable, y un <fieldset> con un <legend> para agrupar campos relacionados. Presta atención a los tipos de botón: submit es el valor por defecto dentro de un formulario.
JunoOtros controles Los radios se agrupan por un name compartido, y un <legend> le da a cada control del fieldset un contexto que se anuncia en voz alta, así que es estructura, no decoración. Dos trampas: un checkbox sin marcar no envía absolutamente nada, no un false, y un <button> sin especificar toma por defecto type="submit", así que va a recargar la página a menos que fijes type="button".

Cómo se nombran y envían los datos de un formulario ​

Cuando se envía un formulario, cada respuesta necesita una etiqueta para que quien la reciba sepa de qué caja vino. Esa etiqueta es el name del input. Lo pones una vez, y viaja junto con el valor.

html
<label for="city">Ciudad</label>
<input id="city" name="city">

Si el visitante escribe "Lima" en esa caja, el formulario envía el par "city es Lima". El name es cómo el lado que recibe distingue una respuesta de otra. Un input sin name queda completamente fuera del envío, así que este pequeño atributo es el que realmente hace que los datos aparezcan.

Los datos de un formulario se envían como un conjunto de pares nombre/valor: el name de cada control se convierte en la clave y lo que escribió el visitante se convierte en el valor. Solo se incluyen los controles que tienen name, así que un name es lo que convierte a un control en dato enviado.

html
<form action="/search" method="get">
  <label for="q">Buscar</label>
  <input id="q" name="q" type="search">
  <label for="sort">Ordenar por</label>
  <select id="sort" name="sort">
    <option value="recent">Más reciente</option>
    <option value="top">Mejor calificado</option>
  </select>
  <button type="submit">Buscar</button>
</form>

El atributo method determina cómo viajan esos pares. method="get" los agrega a la URL como una cadena de consulta (/search?q=botas&sort=recent), lo cual es ideal para búsquedas y filtros que podrías guardar como marcador o compartir. method="post" pone los pares dentro del cuerpo de la solicitud, fuera de vista, lo cual es ideal para cualquier cosa que cambie datos o que no debería aparecer en una URL, como una contraseña o un pago.

Cada envío es un conjunto de pares nombre/valor, y tres detalles determinan cómo se codifican y se envían.

El method es el primero. get serializa los pares dentro de la URL como una cadena de consulta, el texto ?clave=valor&clave=valor que va después de la dirección. Eso hace que la solicitud se pueda repetir y guardar como marcador, y también significa que los valores quedan visibles en la URL, en el historial del navegador, y en los registros del servidor, así que get sirve para lecturas (búsquedas, filtros) y nunca para secretos. post lleva los pares en el cuerpo de la solicitud en lugar de la URL, y es la opción para cualquier cosa que cambie el estado o sea sensible.

El enctype es el segundo: la codificación que indica cómo está formateado el cuerpo. El valor por defecto, application/x-www-form-urlencoded, empaqueta los pares en una sola cadena tipo clave=valor&..., lo cual funciona bien para texto. Para enviar un archivo seleccionado debes cambiar a enctype="multipart/form-data", el formato que puede llevar bytes de archivo junto con campos de texto; un input de tipo archivo no se sube correctamente sin esto.

html
<form action="/upload" method="post" enctype="multipart/form-data">
  <label for="avatar">Foto de perfil</label>
  <input id="avatar" name="avatar" type="file">
  <button type="submit">Subir</button>
</form>

El tercero es autocomplete, y es donde nombrar bien los campos se traduce en mejor experiencia de usuario y accesibilidad. Un token de autocomplete le dice al navegador qué significa un campo en términos estándar, para que pueda ofrecer el valor guardado correcto: autocomplete="email", autocomplete="name", autocomplete="current-password", autocomplete="street-address". Estos tokens son un vocabulario fijo, no texto libre, y usarlos correctamente permite que un navegador o un gestor de contraseñas llene un formulario con un solo toque, lo cual importa muchísimo para quienes escriben desde un celular o usan tecnología de asistencia.

html
<label for="email">Correo</label>
<input id="email" name="email" type="email" autocomplete="email">

Mantener las validaciones incorporadas funcionando correctamente es un tema aparte, que se cubre en el capítulo de validación de formularios; esta sección trata sobre cómo se estructuran y envían los datos una vez que son válidos.

JunoCómo se nombran y envían los datos de un formulario El name de cada input es la etiqueta de su respuesta, así que quien recibe los datos sabe si un valor vino de la caja de ciudad y no de la de correo. Pon el name, y el valor de la caja se envía como un par etiquetado. Olvídalo, y ese campo queda fuera del envío en silencio, lo cual confunde a mucha gente la primera vez que un formulario no devuelve nada.
JunoCómo se nombran y envían los datos de un formulario Los datos enviados son pares nombre/valor, y solo se incluyen los controles con name. Elige el method según la tarea: get pone los valores en la URL para búsquedas y filtros que podrías guardar como marcador, post los oculta en el cuerpo para cualquier cosa que cambie datos o guarde un secreto. Poner contraseñas en una URL es el error que nunca debes cometer.
JunoCómo se nombran y envían los datos de un formularioget escribe los pares en la URL, así que sirve para lecturas y nunca para secretos; post los lleva en el cuerpo. Cambia el enctype a multipart/form-data o un input de archivo no se va a subir. Y dedica esfuerzo a los tokens de autocomplete como email y current-password: son un vocabulario fijo, y usarlos bien permite que un celular o un gestor de contraseñas llene el formulario con un solo toque.