El atributo type decide todo
La etiqueta <input> es siempre la misma. Lo que cambia por completo su comportamiento es un solo atributo: type. Según lo que diga, el navegador dibuja un casillero de una línea, un selector de fecha, un botón de opción o una barra deslizante.
Y no cambia solo el dibujo. El type decide tres cosas a la vez:
- Qué se muestra. Un calendario, un contador con flechas, un campo que oculta lo que se escribe.
- Qué teclado aparece en el celular. Con
type="email"sale el teclado con la arroba a mano; contype="number", el numérico; contype="tel", el de teléfono. - Qué se valida solo. El navegador revisa que un
type="email"tenga forma de correo y que untype="date"sea una fecha real.
Ahí está el motivo por el que esto es un tema aparte y no un detalle: escribir todo con type="text" funciona, y tira a la basura las tres cosas. El teclado del celular aparece completo aunque haya que escribir un número, la validación no existe, y el dato —que llegó como texto libre— va a tener que limpiarse después. La causa más común de datos sucios en una base real no es el SQL: es un campo de texto libre donde podría haber habido un control.
Campos de texto
Son los que dejan escribir libremente, y por eso los que más cuidado requieren. El tipo se elige por el formato esperado:
| Tipo | Para qué | Qué aporta |
|---|---|---|
text | Texto libre de una línea. | Nada especial. Es el último recurso. |
email | Correo electrónico. | Revisa la forma y en el celular pone la arroba a mano. |
password | Contraseñas. | Oculta lo que se escribe con puntos. |
tel | Teléfonos. | Teclado numérico en el celular. No valida nada: los teléfonos tienen mil formatos. |
url | Direcciones web. | Revisa que empiece con un protocolo válido. |
search | Buscadores internos. | Agrega una cruz para borrar lo escrito. |
Del password hay que decir algo que se malinterpreta siempre: ocultar los puntos en pantalla no protege nada. Es una ayuda para que nadie mire por encima del hombro, no una medida de seguridad. Lo que se escribe viaja al servidor tal cual, y por eso una contraseña nunca puede ir en un formulario con método GET.
Y del tel vale la aclaración inversa: es el único tipo que existe solo por el teclado. Un teléfono se escribe de mil maneras —con guiones, con paréntesis, con código de área—, así que el navegador no intenta validarlo y hace bien. Si se quiere controlar el formato, se usa pattern, no el tipo.
Campos numéricos y fechas
Acá está el mayor salto de calidad de un formulario, porque son los datos que se van a usar para calcular y los que peor se cargan a mano.
| Tipo | Qué muestra | Se controla con |
|---|---|---|
number | Un campo con flechas para subir y bajar. | min, max, step |
range | Una barra deslizante. | min, max, step |
date | Un calendario. | min, max |
time | Un selector de hora. | min, max, step |
month | Un mes y un año. | min, max |
color | Un selector de color. | — |
El caso estrella es la fecha. Un campo de texto donde hay que escribir una fecha es un desastre previsible, porque cada persona la escribe distinto: 12/05/2026, 2026-05-12, 12 de mayo. Y el problema no se queda en la estética: 05/12 y 12/05 son dos fechas distintas y no hay manera de saber cuál quiso decir el usuario. Con type="date" eso desaparece: se elige del calendario y el navegador manda siempre el mismo formato.
En los números, step es el atributo que más se olvida. Con type="number" y step="1", un precio con centavos no se puede cargar. Y al revés: step="0.01" en una cantidad de personas deja escribir 3,5. Declarar el paso correcto es parte de definir bien el dato. Y en el celular, min y max no impiden que alguien lo escriba mal, pero el navegador lo marca al enviar.
Casillas y botones de opción
Son dos controles que se parecen en el dibujo y son opuestos en el significado. La diferencia es una sola pregunta: ¿cuántas respuestas puede dar el usuario?
Casilla (checkbox) |
Botón de opción (radio) |
|
|---|---|---|
| Cuántas se marcan | Varias, o ninguna. | Una sola, excluyente. |
El name | Distinto en cada casilla. | El mismo en todas las del grupo. |
| Ejemplos | Materias en las que se inscribe, servicios contratados, intereses. | Turno, estado civil, tipo de documento, plan. |
Esa es toda la diferencia, y está en el name. Al compartir el name, el navegador entiende que los botones de opción son respuestas de la misma pregunta y desmarca el anterior al marcar el siguiente. Si a cada botón se le pone un name distinto, dejan de ser un grupo y se pueden marcar todos a la vez: el formulario se ve bien, funciona, y permite un dato imposible.
El atributo value
Hay un detalle que arruina formularios enteros y que casi nunca se enseña: lo que viaja al servidor es el value, no lo que se lee en el rótulo.
<!-- Mal: si se marca, el servidor recibe "on" y nada más -->
<input type="checkbox" id="acepta" name="acepta">
<label for="acepta">Acepta los términos</label>
<!-- Bien: el servidor recibe "si" -->
<input type="checkbox" id="acepta" name="acepta" value="si">
<label for="acepta">Acepta los términos</label>
Sin value, el navegador manda el texto on para cualquier casilla marcada. Con veinte casillas en un formulario, el servidor recibe veinte veces on y no puede distinguir cuál se marcó. Y hay un segundo detalle de comportamiento: una casilla sin marcar no viaja. No llega con valor cero ni con false: simplemente no está en la lista. Por eso el código del servidor siempre tiene que preguntar «¿llegó?» antes de dar por hecho que llegó un valor.
Listas desplegables
<select> es el control que resuelve el caso de las opciones muchas y conocidas: no se pueden mostrar todas a la vez, y escribirlas a mano sería un error garantizado. Adentro van las opciones, cada una con su value.
<select id="provincia" name="provincia">
<option value="">Elegí una provincia</option>
<option value="ba">Buenos Aires</option>
<option value="cba">Córdoba</option>
<option value="sf">Santa Fe</option>
</select>
Dos cosas de ese ejemplo. La primera: la opción vacía del principio no es decorativa. Sin ella, el navegador muestra la primera opción real como si estuviera elegida, y el usuario que no toca nada manda «Buenos Aires» sin haberlo decidido. Con la opción vacía, el estado inicial es «no eligió nada», que es la verdad. Y encima permite validar con required, porque un <select> con una opción de valor vacío y required no se envía hasta que se elija otra.
La segunda: el value no tiene por qué coincidir con el texto visible. value="cba" es lo que viaja y lo que va a la tabla; «Córdoba» es lo que se lee. Conviene además que el value sea un código corto y estable y no el texto: así, si mañana se corrige la ortografía del rótulo, los datos ya guardados siguen siendo válidos. Para agrupar muchas opciones con títulos, está <optgroup>, que muestra el nombre del grupo sin que se pueda elegir.
Áreas de texto
<textarea> es para respuestas que no entran en un renglón: una observación, una descripción, un comentario. Su diferencia con un campo de texto normal no es solo el tamaño: también permite saltos de línea, y eso significa que lo que se guarda es texto con varios renglones.
<textarea id="observaciones" name="observaciones" rows="5" cols="40"></textarea>
Su contenido inicial no va en un atributo value —el <textarea> no lo tiene— sino entre la etiqueta de apertura y la de cierre. Es un error difícil de detectar, porque el atributo se acepta sin protestar y simplemente no hace nada. Las medidas se dan con rows (renglones de alto) y cols (caracteres de ancho), y aunque se las pueda ajustar con CSS, conviene declararlas igual: es el tamaño que se ve mientras la hoja de estilos carga.
Archivos
<input type="file"> es el único control que no manda el contenido del dato, sino el archivo. Y por eso tiene dos particularidades que no se pueden olvidar.
La primera es del formulario: cuando hay un campo de archivo, la etiqueta <form> necesita un atributo extra.
<form action="/socios/guardar" method="post" enctype="multipart/form-data">
<input type="file" id="foto" name="foto" accept="image/png, image/jpeg">
</form>
Sin enctype="multipart/form-data", el navegador manda el formulario partido en el formato habitual y el archivo no llega. No hay ningún aviso: el servidor recibe todo lo demás y el adjunto aparece vacío. Es el error más común de este control.
accept filtra qué archivos se pueden elegir —image/* para cualquier imagen, .pdf para documentos— y es una comodidad, no una restricción: filtra el diálogo del sistema y nada más. Cualquiera puede elegir «todos los archivos» y mandar otra cosa. Lo mismo vale para el multiple, que permite elegir varios a la vez.
Y la segunda particularidad, que es conceptual y más importante: el archivo no se guarda en la base de datos. La base guarda el nombre o la ruta del archivo —una cadena de texto— y el archivo en sí se guarda en una carpeta del servidor. Guardar las imágenes dentro de la base parece ordenado y es un problema: engorda los respaldos, hace las consultas lentas y complica todo sin ninguna ventaja.
Cuál elegir
Si hay que quedarse con una sola regla, es esta: si las respuestas posibles se conocen de antemano, no se deja escribir libremente. Y si se conocen y son pocas, mejor mostrarlas todas que esconderlas en una lista.
| La situación | El control | Por qué |
|---|---|---|
| Las respuestas son 2 o 3 y se excluyen | Botones de opción | Se ven todas y se eligen con un clic. |
| Las respuestas son 6 o 7 y se excluyen | Lista desplegable | Ya ocupan demasiado espacio para mostrarlas todas. |
| Se pueden marcar varias a la vez | Casillas | Los botones de opción solo admiten una. |
| Es un número dentro de un rango | number o range | Con flechas o barra, se evita el error de tipeo. |
| Es una fecha o una hora | date o time | Se elimina la ambigüedad de formato. |
| Es un importe con centavos | number con step="0.01" | Permite decimales y no cualquier texto. |
| Es una observación libre | textarea | Puede tener varios renglones. |
| Es un adjunto | file | Es el único control que manda un archivo. |
| No hay forma de prever el formato | text, el último recurso | Y entonces habrá que validarlo en el servidor sí o sí. |
El caso del correo merece un comentario aparte porque tiene la forma de un texto libre y no lo es del todo: type="email" no puede verificar que la casilla exista, pero sí que tenga la forma de un correo. Eso solo ya evita el error más común de todos, que es escribir el correo sin la arroba. Para eso está el tipo, y por eso conviene usarlo aunque no valide «de verdad».
Resumen
- El atributo
typedecide qué se muestra, qué teclado aparece en el celular y qué se valida solo. - Usar
type="text"para todo funciona, y descarta las tres cosas. - El tipo se elige por el formato esperado del dato, no por gusto.
type="password"no protege nada: solo tapa lo que se ve en pantalla.- Una fecha con
type="date"elimina para siempre la ambigüedad de 05/12 y 12/05. - Las casillas admiten varias respuestas y los botones de opción una sola. La diferencia está en el
name: igual agrupa, distinto no. - Lo que viaja es el
value, no el rótulo. Sinvalue, una casilla mandaon. - Una casilla sin marcar no viaja: no llega con cero, no llega.
- Una lista desplegable necesita una opción vacía al principio para que el estado inicial sea honesto.
- Un campo de archivo obliga a
enctype="multipart/form-data", y la base guarda la ruta, no el archivo. - Si las respuestas se conocen, no se deja escribir libremente.