30

Quiero que cuando se aprieta mucho tiempo una tecla no se escriba varias veces.

ejemplo:
tecla      tiempo      resultado
  a           3seg        a

Hice este código pero no funciona, use los 3 eventos.

var texto=document.getElementById("texto");
var pulsado=false;
texto.addEventListener('keydown', function(keyboardEvent) {
    if(pulsado) return false;
    pulsado=true;
});
texto.addEventListener('keypress', function(keyboardEvent) {
    if(!pulsado){
                
    }
});
texto.addEventListener('keyup', function(keyboardEvent) {
    pulsado=false;
});
<textarea id="texto"></textarea>
Mariano
  • 23,777
  • 20
  • 70
  • 102
hubman
  • 2,624
  • 9
  • 35
  • 75
  • 1
    Una pregunta: ¿el funcionamiento del textarea debe ser normal pero deshabilitando la repetición automática de las teclas? Me refiero a que si el usuario suelta la tecla y vuelve a presionarla, entonces el texto del elemento debería ser "aa". – Fer García Mar 19 '17 at 20:04
  • @FerGarcía si es lo que busco, publique una respuesta pero no funciona muy bien, cuando escribo rapido no se escribe algunas teclas – hubman Mar 27 '17 at 04:09

7 Answers7

32

Quiero que cuando se aprieta mucho tiempo una tecla no se escriba varias veces
[...]
cuando se escribe rápido, no se escribe todo. como puedo mejorarlo?


En vez de medir tiempos, tenemos que saber cuándo el usuario está repitiendo una tecla. Para ello, usamos la propiedad KeyboardEvent.repeat, que devuelve true cuando no se levantó la tecla y se mantuvo presionada, generando un sucesivo tecleo.

var texto=document.getElementById("texto");

texto.addEventListener('keydown', function(keyboardEvent) {
    //Si se está repitiendo, ignorar
    if (keyboardEvent.repeat)
        keyboardEvent.preventDefault();
});
<textarea id="texto" style="width: 100%; height: 8em"></textarea>


Adaptado a diferentes versiones de Internet Explorer

IE no deja de ser la excepción a la regla (-¿qué raro, no?). En este caso, cuando asociamos el evento con addEventListener(), IE siempre devuelve KeyboardEvent.repeat == false. En cambio, devuelve el valor correcto cuando se utiliza attachEvent...

Pero hay más, attachEvent pasó a ser obsoleto a partir de IE11, con lo cual quedó sin una solución directa. Entonces, para solucionar este segundo problema, usamos la etiqueta meta para el modo de legado X-UA-Compatible: <meta http-equiv="X-UA-Compatible" content="IE=10" />.

Y, de yapa, agregamos algunas excepciones para teclas que normalmente se quiere dejar repetir (backspace, del, flechas, home, etc).

<!DOCTYPE html>
<html>
<head>
    <title>Evitar caracteres repetidos</title>
    <meta http-equiv="X-UA-Compatible" content="IE=10" />
    <style>
        #texto {
            width: 100%;
            height: 8em;
        }
    </style>
</head>
<body>
    <textarea id="texto" placeholder="Mantenga presionada una tecla"></textarea>
    <script language="javascript">
        var texto = document.getElementById("texto"),
            excepcionesTeclas = [
                    8,9,13,46,      //backspace tab enter del
                    33,34,35,36,    //PgUp/Dn home end
                    37,38,39,40     //flechas
                ];

        //attachEvent para IE, addEventListener para el resto
        if (texto.attachEvent) texto.attachEvent('onkeydown', noRepetirTeclas);
        else texto.addEventListener('keydown', noRepetirTeclas);

        function noRepetirTeclas(keyboardEvent) {
            //obtener .repeat según navegador
            var repeat;
            if (window.event && 'repeat' in window.event)
                repeat = window.event.repeat;
            else
                repeat = keyboardEvent.repeat;
            //Si se está repitiendo, ignorar
            // excepcionesTeclas deja repetir backspace, flechas, etc.
            if (repeat && !~excepcionesTeclas.indexOf(keyboardEvent.keyCode)) {
                if (keyboardEvent.preventDefault)
                    keyboardEvent.preventDefault();
                else if ('returnValue' in keyboardEvent)
                    keyboardEvent.returnValue = false; //IE
                else
                    return false; //IE viejo
            }
        }
    </script>
</body>
</html>

* Para IE 8- es necesario usar el Polyfill de Array.prototype.IndexOf().

Demo subida a un hosting gratuito

Mariano
  • 23,777
  • 20
  • 70
  • 102
  • 6
    Ésta es la respuesta más acertada. – Máxima Alekz Mar 27 '17 at 04:34
  • 1
    Yo pruebo el snippet y si dejo una tecla presionada y/o la presiono muchas veces seguidas de todas maneras la escribe, no veo que haga ningùn prevent – Jorius Mar 27 '17 at 15:08
  • 3
    Si se quiere que funcione la tecla de retroceso repetidamente, entonces se podría cambiar la condición por: if (keyboardEvent.repeat && keyboardEvent.key != "Backspace") – Adrià Vilanova Mar 27 '17 at 18:40
  • @Jorius es en caso de presionar una tecla de forma prolongada. No prevenir escritura rápida. – Máxima Alekz Mar 27 '17 at 19:08
  • 1
    @Jorius - Gracias por avisar. Editado para ser compatible con diferentes versiones de IE (por más que nadie debería usar IE). – Mariano Mar 29 '17 at 02:53
8

solucione de acuerdo a una variable booleana: pero cuando se escribe rapido, no se escribe todo. como puedo mejorarlo?

window.onload = function() {

  var pulsado = false,
    input = document.getElementById('input');

  input.addEventListener('keydown', function(e) {
    if (pulsado) e.preventDefault();
    pulsado = true;
  });

  input.addEventListener('keyup', function() {
    pulsado = false;
  });

}
pulsa una tecla <input type="text" id='input'>
hubman
  • 2,624
  • 9
  • 35
  • 75
8

Para resolver esto, lo que hago es verificar si está pulsada cada tecla, en vez de una sola, entonces voy agregando a un objeto la lista de las letras, poniendo una propiedad como letras["f"]=true, cuando se suelta la tecla, se pone en false, que es condición necesaria para poder volver a apretarla.

Como estuvimos hablando en el chat con @Mariano, él ofreció una solución para detectar solo letras y no teclas especiales como Backspace (retroceso). Esto significa que como la palabra Backspace tiene una longitud de 9 caracteres y no exactamente 1, funciona normalmente, mientras que las letras, de manera personalizada.

Al presionar una tecla y si la longitud es 1:

  • Si la tecla fue presionada anteriormente, evita escribir doble,

  • Si no está marcada como presionada, se escribe la letra y luego se marca como presionada.

window.onload = function() {

  var pulsado = false
  var letras={}
  input.addEventListener('keydown',function(e){

    if(letras[e.key]==undefined){letras[e.key]=false}
    if(e.key.length==1)
    {
      if(letras[e.key]){e.preventDefault()}else{letras[e.key]=true}
    }
  })
  input.addEventListener('keyup',function(e){
    letras[e.key]=false
  })
}
Pulsa una tecla: <input type="text" id='input' />
  • 2
    En cada una de las soluciones al pulsar una tecla e intentar pulsar otra no siempre la otra tecla pulsada es mostrada, en algunas soluciones debo esperar, y en otras no funciona despues de la tercera tecla pulsada, esta solución me parece que funciona de maravilla, por eso le doy +1 – Jacobo Córdova Mar 28 '17 at 04:01
  • 1
    @ArtEze muy buena solución, buena imaginacion, funciona!!! – hubman Apr 02 '17 at 22:32
4

Aquí te dejo el ejemplo funcionando, quizás necesite algún que otro retoque si tienes algún otro requerimiento. Básicamente se trata de utilizar los eventos de teclado correctamente teniendo en cuenta cuál se ejecuta antes:

var texto = document.getElementById("texto");
var pulsado = false;
var tiempo_max = 3000; // Max tiempo de pulsación
var ultima_pulsacion; // Fecha ultima pulsación

// KeyDown
texto.addEventListener('keydown', function(keyboardEvent) {
  // Eliminamos la posición del textarea si estamos en el intervalo de tiempo
  if(Date.now() - ultima_pulsacion < tiempo_max){
    texto.value = texto.value.slice(0, -1);
  }
});

// Key Press
texto.addEventListener('keypress', function(e){
  // Si no hay ultima posición
  if(!ultima_pulsacion){
    ultima_pulsacion = Date.now();
  } else if(Date.now() - ultima_pulsacion > tiempo_max){
    // Si hemos superado el intervalo de tiempo ponemos a null la ultima_pulsacion
    ultima_pulsacion = null;
    //texto.value = texto.value.slice(0, -1);
  }
});

// Key Up
texto.addEventListener('keyup', function(e){
  // ponemos a null la última pulsación 
  ultima_pulsacion = null;
});
<textarea id="texto"></textarea>
Jose Hermosilla Rodrigo
  • 3,805
  • 1
  • 15
  • 24
0

Prueba con esto anexe un poco más de codigo para poder lograr el resultado.

    <script>
            var LetraPulsada=""; // la letra que se introduce
            var textoAnterior=''; // el texto anterior a la ultima tecla introducida
            var timer = 0; // timer para contar el tiempo transcurrido
            var intervalo;  // el intervalo
            var textarea; // area de texto donde se introduce el texto
            window.onload = function () // cuando este lista la pagina agregamos los eventos y los listener
            {
                textarea = document.getElementById("texto"); // hacemos referencia al area de texto
                $("#texto").enterKey(function (e) // hacemos el pequeño plugin enterKey declarado al final de este codigo
                {
                    if (LetraPulsada==e.key && timer>1) // verificamos que la letra sea igual a la ultima introducida y que el tiempo transcurrido sea mayor a 1
                    {
                        console.log('= '+e.key+' '+LetraPulsada);
                        $("#texto").val(textoAnterior); // si es true entonces significa que una tecla se ha introducido seguida de una ultima similar por lo tanto negamos la entrada de la letra e introducimos el ultimo texto de la caja
                        clear(); // por si las dudas reiniciamos el conteo
                    }
                    else
                    { // si false entonces introducimos el texto
                        textoAnterior = $("#texto").val();
                        clear(); // por si las dudas reiniciamos el conteo
                    }
                    LetraPulsada=e.key; // guardamos la ultima letra tecleada para hacer uso de ella en las conparaciones de arriba
                });

                textarea.addEventListener("keypress", function()
                {
                    console.log("keypress"+timer);
//  asignamos valor al intervalo y sumamos el timer cada vez que se cumpla el tiempo especificado
                    intervalo = setInterval(function(){timer += 1;}, 1); // 1000 = 1s
                });

                textarea.addEventListener("keyup", function()
                { // cuando se deje de pulsar la tecla reinicamos el intervalo y el timer
                    console.log("keyup");
                    clearInterval(intervalo);
                    timer = 0;
                });
            };
            function clear() // funcion auxiliar para reinicar conteo
            {
                clearInterval(intervalo);
                timer = 0;
            }

            $.fn.enterKey = function (fnc) // pequeño plugin que reconoce si una tecla fue pulsada y retorna objeto con datos de la tecla pulsada
            {
                return this.each(function ()
                {
                    $(this).keypress(function (ev)
                    {
                        var keycode = (ev.keyCode ? ev.keyCode : ev.which);
                        fnc.call(this, ev);
                    })
                })
            }


        </script>
    <textarea id="texto"></textarea>

Cabe mencionar que utilizo jquery y javascript

-1

no necesitas usar los tres eventos, solamente resetea el text-area de manera que siempre vuelva a como estaba al comienzo. Algo asi:

function valida_longitud () {

  let permitido = 0;
  let areaTexto = document.getElementById("texto");

  if (areaTexto.value.length != permitido) {

    areaTexto.value = "";
  }

En el html solo le diras que reaccione cuando se pulse alguna tecla:

      <textarea id="texto" value="" onKeyDown="valida_longitud()"></textarea>

Ahora cada vez que halla mas letras en el text area no se mostraran, a excepción de la ultima letra que fue pulsada

Marco Diaz
  • 113
  • 9
  • 1
    Disculpa, amigo. Pero el " **Preguntador** " se refiere a que no quiere que se repita varias veces el caracter si el usuario presiona una tecla de forma prolongada. – Máxima Alekz Mar 27 '17 at 04:32
-1

Creamos una variable y cuando el usuario presione una tecla la función keydown verificara el estado de la variable, si es true cancelara la repetición, sino ignorara y se volverá true la variable y cuando el usuario la suelte esta variable se volverá false.

Acá mi ejemplo:

 var pulsado = false;
 $("#texto").keydown(function(e){ 
            if(pulsado) return false;
            pulsado = true;
 }).keyup(function(){
            pulsado = false;
  }
 );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<textarea id="texto"></textarea>
bypabloc_
  • 2,346
  • 12
  • 35
  • 89