
⚙️ Cómo restringir la entrada de caracteres no deseados en INPUT con JavaScript
Formulario de registro, campo de teléfono, búsqueda en catálogo: el usuario hace clic en un INPUT y escribe cualquier cosa. Letras donde usted espera dígitos. Caracteres especiales en un número de pedido. Emojis en un campo de correo electrónico. El resultado: basura en la base de datos, consultas rotas, validación del backend que falla con un error.
El problema se resuelve en el frontend con 15 líneas de código. Sin plugins, sin librerías, solo JavaScript simple o un par de atributos HTML. A continuación, tres enfoques funcionales: desde HTML5 rápido hasta el filtrado detallado en keydown, con ejemplos listos para escenarios comunes.
💡 Resumen rápido:
- Examinamos cuándo bastan los atributos HTML
pattern,inputmodeytype, y cuándo se necesita JavaScript - Escribimos un filtro de «solo dígitos» en jQuery y JS simple, ambas versiones con desglose línea por línea
- Añadimos protección contra el pegado mediante Ctrl+V y el menú contextual
- Construimos una función plantilla universal para cualquier restricción: letras, caracteres latinos, máscaras personalizadas
HTML5: lo que puede hacer sin una sola línea de JavaScript
La forma más simple y fiable de restringir la entrada es decirle al navegador exactamente lo que espera en un campo. Los navegadores modernos entienden tres atributos clave que cubren la mayoría de las tareas comunes.
type, el filtro principal. type="number" solo permite dígitos, el signo menos y el separador decimal. type="tel" en dispositivos móviles muestra un teclado numérico. type="email" verifica la presencia de @ al enviar el formulario. type="url" exige un protocolo. Esto no bloquea la entrada por completo, pero proporciona el teclado en pantalla correcto y una validación básica al enviar.
pattern, una expresión regular en el campo. El atributo acepta una expresión regular y valida el valor al enviar el formulario:
1 <input type="text" pattern="[0-9]{6}" placeholder="Six digits" title="Exactly 6 digits">
El navegador bloquea el envío si el valor no coincide con el patrón. La desventaja: pattern no impide introducir caracteres no permitidos; solo valida el resultado. El usuario puede escribir «abc» y solo verá el error al intentar enviar el formulario.
inputmode, el teclado correcto en el móvil. inputmode="numeric" abre un teclado numérico pero no prohíbe introducir letras. inputmode="decimal" añade un separador. Es útil en combinación con un filtro JavaScript: el teclado ya es el correcto y JS se encarga del resto.
Qué elegir. Si necesita impedir realmente la introducción de caracteres no permitidos en tiempo real, los atributos HTML no bastan. Pasemos a JavaScript.
JavaScript: bloqueo de pulsaciones mediante keydown
El enfoque keydown captura cada pulsación antes de que el carácter aparezca en el campo. Si la tecla no está en la lista blanca, event.preventDefault() la suprime.
Opción 1: jQuery, solo dígitos
El código se asocia a un INPUT por su id, permite dígitos y teclas de servicio (retroceso, flechas, Tab) y bloquea todo lo demás:
1 $(document).ready(function() { 2 $("#phone_input").keydown(function(event) { 3 // Service keys — let them through 4 if (event.keyCode === 46 || event.keyCode === 8 || 5 event.keyCode === 9 || event.keyCode === 27 || 6 (event.keyCode === 65 && event.ctrlKey === true) || 7 (event.keyCode >= 35 && event.keyCode <= 39)) { 8 return; 9 } 10 // Block everything except digits (main block and numpad) 11 if ((event.keyCode < 48 || event.keyCode > 57) && 12 (event.keyCode < 96 || event.keyCode > 105)) { 13 event.preventDefault(); 14 } 15 }); 16 });
Esto es lo que ocurre línea por línea: keyCode 46 (Supr), 8 (Retroceso), 9 (Tab), 27 (Escape), 65 + Ctrl (seleccionar todo), 35..39 (Inicio, Fin, flechas). Luego vienen los dígitos del bloque principal 48..57 (0..9) y del teclado numérico 96..105. Todo lo que no entre en estos rangos recibe preventDefault.
Tabla completa de códigos de tecla en Cambia Research.
Opción 2: JavaScript simple, solo dígitos
Lo mismo sin jQuery, usando onkeypress. Esta versión es más compacta y no requiere una librería:
1 document.getElementById("phone_input").onkeypress = function(event) { 2 event = event || window.event; 3 if (event.charCode && (event.charCode < 48 || event.charCode > 57)) { 4 return false; 5 } 6 };
charCode verifica que el evento tenga un carácter imprimible; las teclas de servicio (flechas, retroceso) pasan automáticamente ya que su charCode es igual a 0. Dígitos: 48..57, todo lo demás devuelve false.
Importante: keyCode se considera obsoleto. En código moderno es mejor usar event.key, que devuelve una cadena con el nombre de la tecla y no depende de la distribución del teclado:
1 element.addEventListener("keydown", function(event) { 2 // Allow digits, Backspace, Delete, arrows, Tab 3 const allowedKeys = [ 4 "Backspace", "Delete", "Tab", "Escape", 5 "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", 6 "Home", "End" 7 ]; 8 if (allowedKeys.includes(event.key)) return; 9 // Ctrl+A / Ctrl+C / Ctrl+V 10 if (event.ctrlKey) return; 11 // Only digits 12 if (!/^[0-9]$/.test(event.key)) { 13 event.preventDefault(); 14 } 15 });
El enfoque con event.key se lee con más claridad y no requiere memorizar códigos numéricos. Pero para dar soporte a navegadores antiguos, la versión con keyCode anterior sigue funcionando.
Bloqueo del pegado: capturar Ctrl+V y el menú contextual
keydown captura las pulsaciones, pero el usuario puede pegar texto mediante Ctrl+V o clic derecho → «Pegar». Copió «abc123» en un campo de solo dígitos y la mitad se coló.
La solución es el evento input. Se dispara después de CUALQUIER cambio en el valor del campo: entrada manual, pegado, autocompletado, arrastrar y soltar texto. Filtramos el valor final con una expresión regular:
1 document.getElementById("digits_only").addEventListener("input", function(event) { 2 this.value = this.value.replace(/[^0-9]/g, ""); 3 });
Eso es todo. Una línea, y ni un solo carácter no numérico pasará al campo, independientemente de cómo haya llegado. Para letras: replace(/[^a-zA-Zа-яА-ЯёЁ]/g, ""). Para caracteres latinos: replace(/[^a-zA-Z]/g, "").
Enfoque combinado (el punto medio). keydown proporciona retroalimentación instantánea (la tecla «no se presiona»), mientras que input protege contra el pegado. Use ambos manejadores:
1 const field = document.getElementById("digits_only"); 2 3 field.addEventListener("keydown", function(event) { 4 const allowed = ["Backspace", "Delete", "Tab", "Escape", 5 "ArrowLeft", "ArrowRight", "Home", "End"]; 6 if (allowed.includes(event.key)) return; 7 if (event.ctrlKey) return; 8 if (!/^[0-9]$/.test(event.key)) event.preventDefault(); 9 }); 10 11 field.addEventListener("input", function() { 12 this.value = this.value.replace(/[^0-9]/g, ""); 13 });
El primer manejador corta las pulsaciones no permitidas al instante. El segundo limpia los residuos tras el pegado y el autocompletado.
Plantilla lista para cualquier restricción
Construyamos una función que reciba un INPUT y una expresión regular de caracteres permitidos. Funciona para números de teléfono, códigos postales, inicios de sesión, búsqueda de SKU:
1 function restrictInput(element, allowedPattern) { 2 element.addEventListener("keydown", function(event) { 3 const serviceKeys = [ 4 "Backspace", "Delete", "Tab", "Escape", 5 "ArrowLeft", "ArrowRight", "Home", "End" 6 ]; 7 if (serviceKeys.includes(event.key)) return; 8 if (event.ctrlKey && ["a", "c", "v", "x", "z"].includes(event.key.toLowerCase())) return; 9 if (!allowedPattern.test(event.key)) event.preventDefault(); 10 }); 11 12 element.addEventListener("input", function() { 13 // Build a global pattern from the provided one and clean 14 const globalPattern = new RegExp( 15 allowedPattern.source.replace("^", "").replace("$", ""), 16 "g" 17 ); 18 // Remove everything that does NOT match the pattern 19 const inverted = new RegExp( 20 "[^" + allowedPattern.source.slice(1, -1) + "]", "g" 21 ); 22 this.value = this.value.replace(inverted, ""); 23 }); 24 }
Uso:
1 // Digits only 2 restrictInput(document.getElementById("phone"), /^[0-9]$/); 3 // Latin letters only without spaces 4 restrictInput(document.getElementById("login"), /^[a-zA-Z]$/); 5 // Letters and digits 6 restrictInput(document.getElementById("postal"), /^[a-zA-Z0-9]$/);
La función no depende de jQuery, funciona en todos los navegadores modernos y cubre tres escenarios: entrada manual (keydown), pegado (input), autocompletado (input).
Nota: keydown no se dispara en teclados virtuales de algunos dispositivos móviles; ahí input sigue siendo la única salvaguarda. Por eso dos manejadores juntos no es redundancia, sino necesidad.
El vídeo anterior es un desglose de la validación de formularios en JavaScript simple de Web Dev Simplified. Cubre la API de validación de restricciones integrada, mensajes de error personalizados y el estilo de campos no válidos, un gran complemento a los enfoques aquí descritos.
⁉️🤔 Preguntas frecuentes
¿Por qué no usar simplemente type="number"?
type="number"permite introducireyE(exponente), más, menos y punto. Todos estos son caracteres válidos para números de coma flotante y notación exponencial. Para un campo de teléfono o código postal, son innecesarios. Además, los navegadores manejan la entrada no numérica de forma diferente: algunos la dejan pasar en silencio, otros muestran un error solo al enviar.
¿Se puede bloquear completamente el pegado mediante Ctrl+V?
Bloquear el pegado por completo es una mala práctica. Rompe la experiencia de usuario y no protege contra otros métodos de pegado (menú contextual, arrastrar y soltar). El enfoque correcto: permitir el pegado, pero limpiar inmediatamente el resultado mediante un manejador
input, como se muestra en la sección anterior.
¿Qué pasa con el autocompletado del navegador?
El autocompletado se dispara antes que
keydowneinput; el navegador inserta el valor directamente. El manejadorinputtambién captura este caso: el valor aparece en el campo y luego se filtra de inmediato. Si el autocompletado interfiere (insertando letras en un campo de teléfono), añada el atributoautocomplete="off"al INPUT.
¿Funciona esto en dispositivos móviles?
Sí, pero con una salvedad: los teclados virtuales no siempre generan
keydownconkeyCodeykeyesperados. El manejadorinputfunciona de forma fiable en todas partes. En móvil, establezcainputmode="numeric"oinputmode="decimal"en el INPUT; el usuario obtendrá el teclado correcto incluso si el filtrokeydownno se dispara.
¿Cómo se prohíben los espacios?
Añada el espacio a la clase de caracteres no permitidos:
replace(/[^0-9a-zA-Z]/g, "")elimina todo excepto dígitos y letras latinas, incluidos los espacios. O explícitamente:this.value = this.value.replace(/\s/g, "")eliminará todos los espacios, tabulaciones y saltos de línea.
Qué enfoque elegir para su tarea

Si el formulario es simple y un par de caracteres incorrectos no causarán un daño crítico, use HTML5: type, pattern e inputmode cubrirán los escenarios básicos sin una sola línea de JS. Rápido, nativo, sin dependencias.
Si importa el control en tiempo real y la reacción instantánea a cada pulsación, use la combinación keydown + input. El primero bloquea las pulsaciones, el segundo limpia las consecuencias del pegado. La función plantilla universal de la sección anterior se adapta a cualquier campo en un minuto.
La regla principal: no bloquee el pegado por completo. Molesta a los usuarios y no proporciona una protección real. Limpie el resultado después de pegar en lugar de prohibirlo.



