Curso de HTML / Placeholder y ayuda persistente en los formularios

El atributo placeholder muestra una pista breve dentro de un campo vacío; label identifica el campo y value puede aportar un valor inicial real. La pista desaparece cuando hay un valor, por lo que no reemplaza una etiqueta ni una instrucción necesaria durante la escritura. Separar esas funciones evita confundir un ejemplo de respuesta con datos que se enviarán.

Distinguir etiqueta, pista y valor inicial

Recurso Función Qué ocurre al escribir Relación con los datos
label Identifica el campo Sigue disponible Su texto no es el valor del input
placeholder Ofrece un ejemplo breve Deja de mostrarse si hay un valor La pista no se envía como valor
value en input Establece un valor inicial Se edita como contenido del campo Puede enviarse aunque no se haya cambiado

“Correo electrónico” es una etiqueta; “p. ej., alex@example.com” es una pista. En un formulario nuevo, no pongas una dirección de muestra en value solo para explicar el formato: puede parecer una dirección real y terminar enviada. En cambio, un formulario de edición de cuenta sí puede necesitar el valor guardado como contenido inicial.

Añadir una pista a input y textarea

Escribe placeholder en la etiqueta de apertura del input. Conserva también la etiqueta y su asociación con el identificador:

<label for="email">Email</label>
<input type="email" id="email" name="email"
       placeholder="e.g. alex@example.com">

for se relaciona con id; name identifica el dato enviado. La asociación de label e input permite comparar esos papeles. HTML contempla placeholder para tipos como text, search, tel, url, email y password; no dependas del mismo comportamiento en controles especializados como fechas.

textarea también admite una pista:

<label for="message">Message</label>
<textarea id="message" name="message" rows="4"
          placeholder="e.g. When will my order arrive?"></textarea>

Si escribes contenido entre sus etiquetas, ese contenido se convierte en valor inicial, no en un placeholder:

<textarea name="message">Please tell me when my order will arrive.</textarea>

Cuando una pista no aparece, revisa texto o espacios sobrantes, un valor inicial, el autocompletado y cualquier script que rellene el campo. En textarea, la ubicación del valor inicial es distinta del atributo value de input.

Mantener las instrucciones disponibles

Una condición importante debe permanecer visible durante la edición. aria-describedby puede relacionar el control con el id del texto que amplía su explicación:

<label for="email">Email (required)</label>
<input type="email" id="email" name="email"
       placeholder="e.g. alex@example.com"
       aria-describedby="email-help" required>
<p id="email-help">Enter the email address where you want to receive a reply.</p>

El atributo permite que las tecnologías de apoyo encuentren la descripción, pero no crea el texto ni implementa validación. Mantén la explicación visible y comprueba que su ID coincide. La guía de instrucciones de W3C WAI desarrolla este patrón.

Dos estados de un formulario: al escribir el correo desaparece la pista, mientras la etiqueta y la explicación siguen visibles

El esquema compara el campo vacío y el mismo campo con un correo escrito. La pista del correo desaparece; la etiqueta y la explicación siguen disponibles.

Probar un formulario completo en español

Guarda este código como form-guide.html en UTF-8 y ábrelo en el navegador. El documento declara español, conserva etiquetas visibles y limita la anchura de los campos con CSS.

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Ejemplo de instrucciones en un formulario</title>
  <style>
    body {
      margin: 24px;
      color: #222;
      font-family: sans-serif;
      line-height: 1.6;
    }
    form { max-width: 440px; }
    label { display: block; font-weight: bold; }
    input, textarea {
      box-sizing: border-box;
      width: 100%;
      padding: 10px;
      border: 1px solid #777;
      border-radius: 4px;
      font: inherit;
    }
    input::placeholder, textarea::placeholder {
      color: #666;
      opacity: 1;
    }
    .help { margin-top: 4px; }
    button { padding: 8px 20px; font: inherit; }
  </style>
</head>
<body>
  <h1>Formulario de contacto</h1>
  <form method="get">
    <p>
      <label for="email">Correo electrónico (obligatorio)</label>
      <input type="email" id="email" name="email"
             placeholder="p. ej., alex@example.com"
             aria-describedby="email-help" required>
    </p>
    <p id="email-help" class="help">Escribe el correo donde quieres recibir una respuesta.</p>
    <p>
      <label for="message">Mensaje (opcional)</label>
      <textarea id="message" name="message" rows="4"
                placeholder="p. ej., ¿Cuándo llegará mi pedido?"></textarea>
    </p>
    <button type="submit">Comprobar datos</button>
  </form>
</body>
</html>

Primero observa las dos pistas. Escribe una dirección ficticia y comprueba que la etiqueta del correo y la explicación permanecen. Borra el valor por completo: la pista debe aparecer de nuevo.

Intenta enviar con el correo vacío y después con abc. En el primer caso actúa required; en el segundo, el formato de email. Usa luego alex@example.com y deja vacío el mensaje opcional. El navegador permite superar esas restricciones, pero un formato correcto no demuestra que el buzón exista. La validación con valores límite separa cada condición.

El ejemplo no contiene un servidor que guarde mensajes. Al omitir action, GET apunta al documento actual. Una página servida por HTTP puede mostrar los valores en la consulta de la URL; la navegación de un archivo local puede variar. Un servicio real necesita un destino y un método apropiados.

Comprobar qué valor entra en los datos

Prueba el siguiente fragmento en otro archivo para comparar un valor inicial con una pista:

<form id="compare-form">
  <label for="default-name">Initial value</label>
  <input id="default-name" type="text" name="default_name" value="Alex">
  <label for="hint-name">Hint example</label>
  <input id="hint-name" type="text" name="hint_name" placeholder="e.g. Alex">
</form>

Desde Console puedes construir e inspeccionar la lista sin enviarla al servidor:

Array.from(new FormData(document.getElementById("compare-form")).entries())

Con los dos campos sin editar, el resultado esperado es:

[["default_name", "Alex"], ["hint_name", ""]]

“Alex”, escrito en value, se incluye. La pista “e.g. Alex” no se convierte en contenido: el segundo campo aporta una cadena vacía. Si escribes Sam en él, su valor pasa a ser Sam. Si editas el primero, se utiliza su valor actual, no se impone siempre el inicial.

Un campo vacío habilitado y con name puede aportar un dato vacío; un control sin nombre o deshabilitado normalmente se omite. Construir FormData no ejecuta por sí solo la validación interactiva de required. La estructura del envío de formularios explica esas diferencias.

Resolver pistas invisibles o poco legibles

Comprueba primero si ya hay un valor y si el tipo admite la pista. Mantén el ejemplo breve; deja condiciones detalladas fuera del campo y evita saltos de línea en un placeholder de input.

El CSS ::placeholder permite ajustar color y opacidad, pero una apariencia muy pálida puede dificultar la lectura. Prueba el contraste con el fondo real, un ancho estrecho y ampliación. Conserva etiquetas para los nombres, pistas para ejemplos cortos y descripciones visibles para instrucciones que se necesitan mientras se escribe. La definición de placeholder en el estándar HTML especifica su alcance.