0Apoyar

Playground de input en HTML

Explora los 22 tipos de input, sus atributos y la validación nativa. Compara valores, estados y datos del formulario sin enviar nada a un servidor.

Leer la guía

Preguntar a la IA

Playground de input en HTML

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.

Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
HTML
<form id="input-demo" class="input-demo" method="post" action="#">
  <label for="input-demo-field">Campo de ejemplo (Obligatorio)</label>
  <p id="input-demo-help">Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.</p>
  <input
    id="input-demo-field"
    type="email"
    name="example"
    aria-describedby="input-demo-help input-demo-error"
    placeholder="you@example.com"
    autocomplete="email"
    required
    autocapitalize="off"
    spellcheck="false"
  >
  <p id="input-demo-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
JS
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-demo");
const field = document.getElementById("input-demo-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Guía educativa de input

Elegir el tipo de campo, validar y enviar datos

Elige el tipo por el dato que necesitas recoger y comprende cómo se relacionan interfaz, restricciones y envío. Un picker cómodo no garantiza un dato válido en tu servidor.

Volver al playground

Modelo mental

Un mismo elemento: semántica, restricciones, validez y datos.

  1. 1. Dato y etiqueta

    label + type + instrucciones visibles

  2. 2. Restricciones

    required · pattern · límites · reglas propias

  3. 3. Validación y envío

    ValidityState → FormData → validación de servidor

label → type → validity → FormDataUn mismo elemento: semántica, restricciones, validez y datos.

type define la semántica y la interfaz, los atributos añaden restricciones, el navegador decide la validez y name identifica el dato que participa en el formulario. value en HTML establece el valor inicial; la propiedad value refleja el valor actual. checked tiene la misma distinción con defaultChecked.

Los 22 tipos de input

Catálogo completo de tipos actuales. datetime es obsoleto; datetime-local sí es vigente.

type="text"
Texto de una línea. Tipo por defecto si type falta o no se reconoce. Para identificadores, códigos postales o tarjetas usa texto y pistas de teclado.
type="tel"
Teléfono. Puede ofrecer teclado telefónico; no impone un formato universal. Usa instrucciones y pattern si tu dominio exige un formato concreto.
type="url"
URL absoluta. Detecta typeMismatch si el valor no tiene sintaxis de URL; no comprueba que el recurso exista ni que sea seguro.
type="email"
Dirección de correo. Comprueba sintaxis básica, no existencia ni propiedad. multiple permite una lista separada por comas.
type="password"
Texto visualmente oculto. Permite minlength, maxlength y pattern. No cifra, no impide pegar y no sustituye HTTPS; usa autocomplete apropiado.
type="number"
Cantidad numérica. min/max limitan el intervalo y step la granularidad. No admite pattern ni maxlength; no lo uses para conservar ceros iniciales.
type="range"
Valor numérico aproximado. Por defecto min=0, max=100 y step=1; el navegador ajusta y limita el valor. required y readonly no aplican.
type="date"
Fecha de calendario. value usa YYYY-MM-DD aunque el selector se presente en el idioma local. min/max son fechas y step se expresa en días.
type="month"
Año y mes con formato YYYY-MM; step en meses. Soporte limitado: puede degradar a texto sin aplicar límites temporales.
type="week"
Año y semana ISO con formato YYYY-Www; step en semanas. Soporte limitado: puede degradar a texto. La semana ISO puede pertenecer a otro año civil.
type="time"
Hora sin zona, HH:mm o HH:mm:ss. step se expresa en segundos, 60 por defecto. El formato enviado no depende del reloj de 12/24 horas de la interfaz.
type="datetime-local"
Fecha y hora locales: YYYY-MM-DDTHH:mm. No contiene zona horaria ni convierte a UTC; conserva la zona por separado cuando el producto la necesite.
type="color"
Selector de color. #RRGGBB es la base interoperable. alpha y colorspace amplían la entrada con soporte parcial; no presupongas soporte por reconocer type=color.
type="checkbox"
Selección independiente. Solo aporta name=value si está marcada y habilitada; sin value se envía on. required exige marcar esa casilla.
type="radio"
Una opción por grupo de radios con el mismo name y propietario de formulario. required exige una selección en el grupo; usa fieldset y legend para la pregunta común.
type="file"
Selección local de archivos. accept orienta el selector, multiple permite varios y capture puede sugerir una cámara. value no permite precargar un archivo.
type="hidden"
Dato sin interfaz y fuera de validación. Su valor sigue siendo visible y manipulable con herramientas del navegador; nunca es un secreto ni una prueba de autorización.
type="button"
Botón sin acción predeterminada; value aporta su nombre visible. Necesita JavaScript para realizar una acción. button permite contenido más rico.
type="submit"
Envía el formulario y activa validación salvo novalidate/formnovalidate. Solo el submitter activado aporta su propio name=value.
type="reset"
Restaura valores y checked iniciales del formulario; no borra necesariamente los campos ni elimina errores de setCustomValidity. Puede provocar pérdida de trabajo.
type="image"
Botón de envío gráfico. Requiere src y alt descriptivo. Aporta coordenadas name.x/name.y del clic, no el value de un botón submit ordinario.

Atributos y restricciones

Un atributo puede existir en el DOM y no tener efecto sobre el tipo actual.

required exige contenido en los tipos que lo admiten. En checkbox exige marcar esa casilla y en radio elegir una opción del grupo. No afecta a hidden, range, color ni botones. Un campo opcional vacío suele ser válido: pattern y minlength no sustituyen a required.

pattern se aplica a text, search, tel, url, email y password, coincide con todo el valor y usa sintaxis de expresión regular con bandera v, sin delimitadores /…/. Explica el formato en texto visible; title por sí solo no basta. Un patrón inválido se ignora, no valida de forma más estricta.

minlength/maxlength se aplican a los tipos de texto y cuentan unidades de código UTF-16, no necesariamente caracteres percibidos. Sus restricciones dependen de la edición del usuario; asignar value desde JavaScript no reproduce tooShort/tooLong. maxlength puede impedir teclear más, en lugar de producir un error visible.

min/max/step se aplican a number, range y tipos temporales. step se calcula desde min, desde el atributo value inicial o desde una base propia del tipo; no basta con comprobar divisibilidad desde cero. number/range usan step=1 por defecto; date usa días, month meses, week semanas y time/datetime-local segundos (60 por defecto). any elimina la restricción de incremento.

autocomplete identifica el dato esperado para facilitar el autocompletado en ordenador y móvil. Por ejemplo, email, username y one-time-code describen datos distintos. Es una pista, no un validador; off puede ser ignorado por gestores de contraseñas.

inputmode y enterkeyhint sugieren la distribución y la etiqueta de Intro del teclado virtual, habitual en móviles y tabletas. No modifican el teclado físico ni validan; enterkeyhint no implementa acciones por sí solo.

placeholder no sustituye label. size controla presentación en tipos compatibles; para layout responsive usa CSS. list conecta sugerencias con datalist sin limitar los valores permitidos.

Validación nativa y personalizada

Distingue la validez técnica del momento de mostrar un error.

validity expone valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow, stepMismatch, badInput y customError; valid resume el resultado. willValidate indica si el control participa. validationMessage depende del navegador y su idioma. checkValidity() devuelve un booleano y dispara invalid; reportValidity() además solicita la interfaz de error nativa.

setCustomValidity("mensaje") mantiene customError mientras el mensaje sea no vacío. Corrige la regla y llama a setCustomValidity("") para quitarlo; cambiar value o resetear el formulario no lo borra automáticamente. El playground permite fijar un error de demostración y eliminarlo vaciando su control.

No muestres un error al cargar un campo obligatorio vacío. Da instrucciones antes del campo, comunica errores tras salir o intentar enviar y limpia el mensaje visual al reanudar la edición. :user-invalid permite estilo tras interacción; acompaña el estilo con texto, aria-invalid y una descripción asociada.

Qué se envía y qué se excluye

La validez y la presencia en FormData son cuestiones distintas.

name es la clave del dato: un control sin nombre no aporta entrada. disabled se excluye de FormData y validación; readonly se puede enfocar y se envía, pero no se valida. Checkbox/radio sin marcar se excluyen. Un hidden habilitado con nombre se incluye aunque no se vea. FormData no comprueba validez por sí solo. Evita nombres como submit o reset que pueden ocultar métodos del formulario.

requestSubmit() reproduce un envío con validación y evento submit; form.submit() los omite. novalidate en form y formnovalidate en un submitter omiten validación interactiva al enviar, pero reportValidity() sigue comprobándola. new FormData(form, submitter) incluye el botón activado; type=image aporta coordenadas. En esta demo el evento submit se cancela y los datos se muestran localmente.

form puede asociar el control a un formulario por id aunque esté fuera de él. action, method y enctype se definen en el formulario; formaction, formmethod, formenctype, formtarget y formnovalidate permiten sobrescribir el envío en submit/image. Usa GET para consultas idempotentes y POST para datos sensibles o cambios; los archivos necesitan multipart/form-data en un envío HTML nativo. Valida y autoriza siempre en servidor.

Archivos y selectores nativos

Seleccionar no equivale a cargar ni a validar contenido.

files contiene objetos File. accept filtra u orienta la selección, pero no valida tipo real, tamaño ni contenido; compruébalos en servidor. capture es una pista de captura para dispositivos compatibles. No puedes establecer una ruta local con value, ni conocer la ruta real del sistema. La demo solo muestra nombres, tipos y tamaños, sin leer contenido.

Ejemplos nativos y reutilizables

Escenas independientes del playground. Comprobar y enviar solo actúan localmente. El HTML copiado usa POST y el JS cancela el envío de demostración.

Correo obligatorio

Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Correo obligatorio
<form id="input-guide-example-email" class="input-demo" method="post" action="#">
  <label for="input-guide-example-email-field">Campo de ejemplo (Obligatorio)</label>
  <p id="input-guide-example-email-help">Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.</p>
  <input
    id="input-guide-example-email-field"
    type="email"
    name="example"
    aria-describedby="input-guide-example-email-help input-guide-example-email-error"
    placeholder="you@example.com"
    autocomplete="email"
    required
    autocapitalize="off"
    spellcheck="false"
  >
  <p id="input-guide-example-email-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-email");
const field = document.getElementById("input-guide-example-email-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Código que conserva ceros

Exactamente cuatro dígitos (0–9); se permiten ceros iniciales. Restricciones aplicadas: pattern=[0-9]{4}; minlength=4; maxlength=4.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Código que conserva ceros
<form id="input-guide-example-code" class="input-demo" method="post" action="#">
  <label for="input-guide-example-code-field">Campo de ejemplo (Obligatorio)</label>
  <p id="input-guide-example-code-help">Exactamente cuatro dígitos (0–9); se permiten ceros iniciales. Restricciones aplicadas: pattern=[0-9]{4}; minlength=4; maxlength=4.</p>
  <input
    id="input-guide-example-code-field"
    type="text"
    name="example"
    aria-describedby="input-guide-example-code-help input-guide-example-code-error"
    placeholder="0123"
    pattern="[0-9]{4}"
    minlength="4"
    maxlength="4"
    autocomplete="one-time-code"
    inputmode="numeric"
    required
  >
  <p id="input-guide-example-code-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-code");
const field = document.getElementById("input-guide-example-code-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Cantidad e incrementos

Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida. Restricciones aplicadas: min=2; max=12; step=2.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Cantidad e incrementos
<form id="input-guide-example-number" class="input-demo" method="post" action="#">
  <label for="input-guide-example-number-field">Campo de ejemplo (Obligatorio)</label>
  <p id="input-guide-example-number-help">Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida. Restricciones aplicadas: min=2; max=12; step=2.</p>
  <input
    id="input-guide-example-number-field"
    type="number"
    name="example"
    aria-describedby="input-guide-example-number-help input-guide-example-number-error"
    value="4"
    min="2"
    max="12"
    step="2"
    required
  >
  <p id="input-guide-example-number-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-number");
const field = document.getElementById("input-guide-example-number-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Elección de una opción

Opción principal

Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Elección de una opción
<form id="input-guide-example-radio" class="input-demo" method="post" action="#">
  <fieldset>
    <legend>Opción principal</legend>
    <label for="input-guide-example-radio-field">Primera opción (Obligatorio)</label>
    <p id="input-guide-example-radio-help">Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida.</p>
    <input
      id="input-guide-example-radio-field"
      type="radio"
      name="example"
      aria-describedby="input-guide-example-radio-help input-guide-example-radio-error"
      value="accepted"
      required
    >
    <label for="input-guide-example-radio-second">Segunda opción</label>
    <input id="input-guide-example-radio-second" type="radio" name="example" value="alternative" aria-describedby="input-guide-example-radio-help input-guide-example-radio-error" required>
  </fieldset>
  <p id="input-guide-example-radio-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-radio");
const field = document.getElementById("input-guide-example-radio-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Archivos con selección múltiple

Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida. Restricciones aplicadas: accept=image/*,.pdf.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Archivos con selección múltiple
<form id="input-guide-example-files" class="input-demo" method="post" action="#" enctype="multipart/form-data">
  <label for="input-guide-example-files-field">Campo de ejemplo (Obligatorio)</label>
  <p id="input-guide-example-files-help">Usa datos ficticios. Nada de lo escrito aquí se envía ni se incluye en la URL compartida. Restricciones aplicadas: accept=image/*,.pdf.</p>
  <input
    id="input-guide-example-files-field"
    type="file"
    name="example"
    aria-describedby="input-guide-example-files-help input-guide-example-files-error"
    accept="image/*,.pdf"
    required
    multiple
  >
  <p id="input-guide-example-files-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-files");
const field = document.getElementById("input-guide-example-files-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Un valor readonly sí se incluye

El valor no se puede editar, pero aparece en los datos del formulario.

Interactúa con el campo o comprueba su validez.

Sin entradas para este control.
Edita el campo y usa Comprobar validez o Probar envío local; observa el mensaje y los datos del formulario.
Un valor readonly sí se incluye
<form id="input-guide-example-readonly" class="input-demo" method="post" action="#">
  <label for="input-guide-example-readonly-field">Campo de ejemplo</label>
  <p id="input-guide-example-readonly-help">El valor no se puede editar, pero aparece en los datos del formulario.</p>
  <input
    id="input-guide-example-readonly-field"
    type="text"
    name="example"
    aria-describedby="input-guide-example-readonly-help input-guide-example-readonly-error"
    value="BL-2026"
    readonly
  >
  <p id="input-guide-example-readonly-error" data-input-error role="status"></p>
  <div class="demo-actions">
    <button type="button" data-check>Comprobar validez</button>
    <button type="submit">Probar envío local</button>
    <button type="reset">Restaurar ejemplo</button>
  </div>
  <p data-input-status role="status">Interactúa con el campo o comprueba su validez.</p>
  <pre data-input-data aria-label="Datos del formulario">Sin entradas para este control.</pre>
</form>
// Local demonstration: replace with your server endpoint in a real form.
const form = document.getElementById("input-guide-example-readonly");
const field = document.getElementById("input-guide-example-readonly-field");

// Named controls may shadow form methods (for example name="submit").
const query = (selector) => Element.prototype.querySelector.call(form, selector);

const on = (type, listener, capture = false) => EventTarget.prototype.addEventListener.call(form, type, listener, capture);

const error = query('[data-input-error]');
const status = query('[data-input-status]');
const output = query('[data-input-data]');

field.setCustomValidity("");

function showValidity() {
  const invalid = field.willValidate && !field.validity.valid;
  field.setAttribute('aria-invalid', String(invalid));
  error.textContent = invalid ? field.validationMessage : '';
  status.textContent = !field.willValidate ? "Este control no participa en la validación (willValidate = false)." : invalid ? "El valor no cumple las restricciones." : "Válido según las restricciones actuales.";
}

field.addEventListener('input', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
});

field.addEventListener('blur', showValidity);

on('invalid', showValidity, true);

query('[data-check]').addEventListener('click', () => {
  HTMLFormElement.prototype.reportValidity.call(form);
  showValidity();
});

on('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form, event.submitter);
  output.textContent = JSON.stringify(Array.from(data, ([name, value]) => [name,
    value instanceof File ? { name: value.name, size: value.size, type: value.type } : value
  ]), null, 2);
  status.textContent = "Envío local simulado. No se ha realizado ninguna petición.";
});

on('reset', () => {
  field.removeAttribute('aria-invalid');
  error.textContent = '';
  output.textContent = "Sin entradas para este control.";
  status.textContent = "Interactúa con el campo o comprueba su validez.";
});

Accesibilidad y errores frecuentes

La semántica nativa ayuda; hay que verificar el flujo completo.

Asocia cada campo visible a label; para grupos usa fieldset/legend. Explica formato y obligatoriedad, enlaza ayuda y errores con aria-describedby y conserva foco visible, teclado y zoom. No ocultes un input operable con display:none para dibujar uno falso. Los botones necesitan nombre accesible y image necesita alt. No bloquees pegar ni el autocompletado útil.

Evita usar number para un código postal, validar teléfonos con un formato universal, exigir que email demuestre existencia, tratar hidden como secreto o confiar en accept como defensa. La validación del navegador mejora la experiencia; el servidor protege los datos. Prueba también sin JavaScript, con tecnologías de asistencia y en los dispositivos objetivo.

Continuar con datalist y sus sugerencias

Soporte y compatibilidad

Comprueba cada funcionalidad y conserva una alternativa usable.

La disponibilidad general de input y Constraint Validation API no certifica todos sus tipos ni todas sus interfaces. month y week tienen soporte limitado y pueden degradar a text: ofrece formato visible, una alternativa usable y validación de servidor. Detectar que input.type conserva un valor solo verifica reconocimiento; no certifica la calidad o accesibilidad de un picker.

color básico se usa con #RRGGBB. alpha y colorspace permiten formatos de color más amplios en motores compatibles y pueden ignorarse en otros. La guía no garantiza selectores de cámara, color o fecha iguales en todos los sistemas. datetime-local no conoce zona horaria.