4

Quiero que se ejecute determinada función cuando presione una tecla, cualquiera. Pero que se ejecute una vez (en el momento en que la presione). Esperaba hacer esto con "keydown" pero se comporta igual "keypress"

addEvent(document, "keypress", function (e) {
    console.log("keypress");
});
addEvent(document, "keydown", function (e) {
    console.log("keydown");
});
addEvent(document, "keyup", function (e) {
    console.log("keyup");
});

function addEvent(element, eventName, callback) {
    if (element.addEventListener) {
        element.addEventListener(eventName, callback, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + eventName, callback);
    } else {
        element["on" + eventName] = callback;
    }
}

PD: La documentación respecto a la funcion addEvent, se encuentra en esta pregunta

Ivan Botero
  • 6,513
  • 9
  • 33
  • 53
Theia
  • 786
  • 2
  • 8
  • 24

3 Answers3

6

De acuerdo a esta documentacion:

Estos eventos se generan cuando el usuario presiona las teclas. El evento onKeyDown sucede cuando el usuario pulsa una tecla. El evento onKeyUp tiene lugar cuando el usuario deja de pulsar una tecla. El evento onKeyPress se genera cuando se mantiene pulsada una tecla.

Si lo que necesitas es asociar un evento para cuando presionas una tecla, lo que deberias usar es onKeyDown(), ya que este es el que se ejecuta cuando se presiona una tecla, mas no cuando se mantiene presionada.

Podrias hacer algo como esto:

addEvent(document, "keydown", function (e) {
    console.log("Se presiono una tecla");
});

function addEvent(element, eventName, callback) {
    if (element.addEventListener) {
        element.addEventListener(eventName, callback, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + eventName, callback);
    } else {
        element["on" + eventName] = callback;
    }
}

onKeyUp() se ejecutara cuando la tecla se "libere", es decir cuando el usuario deje de presionarla. Como por ejemplo:

function cambiaMayuscula(elemento){
 
    elemento.value = elemento.value.toUpperCase();

}
<input type="text" onkeyup="cambiaMayuscula(this)" placeholder="Escribe algo...">

Mira lo que ocurre ahora si usamos onKeyDown(), escribe algo en la caja de texto:

function cambiaMayuscula(elemento){
 
    elemento.value = elemento.value.toUpperCase();

}
<input type="text" onkeydown="cambiaMayuscula(this)" placeholder="Escribe algo...">

En esta funcion convertimos el texto escrito por el usuario a MAYUSCULA, pero mira lo que pasa cuando escribes.


De acuerdo a lo que me indicas en los comentarios (que solo se ejecute una vez), creo que podrias usar algo como esto:

var teclaPresionada = false;

addEvent(document, "keydown", function (e) {

  if(!teclaPresionada){
  
    console.log("Tecla Presionada");
  
    teclaPresionada = true;
  }
  
});

addEvent(document, "keyup", function (e) {

  if(teclaPresionada){
  
    console.log("Tecla Liberada");
  
    teclaPresionada = false;
  }
  
});


function addEvent(element, eventName, callback) {
    if (element.addEventListener) {
        element.addEventListener(eventName, callback, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + eventName, callback);
    } else {
        element["on" + eventName] = callback;
    }
}
Ivan Botero
  • 6,513
  • 9
  • 33
  • 53
  • Depende mayormente del toolkit del sistema, `keydown` es siempre llamado mientras la tecla no se suelte, `keypress` es el que solo se llama cuando se da la combinación de los eventos `keydown + keyup`, es estándar, hay varias formas de lidiar cuando keydown es un alias de keypress como hice anteriormente en mi respuesta, lo díficil es la detección del mismo. – Eduen Sarceño Jun 30 '17 at 22:33
  • Hay alguna forma de hacer que se ejecute una sola vez antes de cada "keyup". O sea cada vez que se presione, no cada instante que este presionada una tecla – Theia Jul 01 '17 at 02:28
  • El onKeyDown se ejecuta cuando esta presionada. – Ivan Botero Jul 01 '17 at 02:31
  • Si, necesito que sea una sola vez. No mientras este presionada – Theia Jul 01 '17 at 03:09
  • He agregado algo que quiza podra servirte – Ivan Botero Jul 01 '17 at 03:22
  • @Theia observa el segundo fragmento de mi respuesta, veras que keypress es llamado una única vez no importando las veces que se llame a keydown – Eduen Sarceño Jul 01 '17 at 07:20
  • @EduenSarceño ya, solo faltaria que on "keyup" `teclaPresionada = false`. y asi cada vez que se suelte la tecla se pueda volver a presionar – Theia Jul 01 '17 at 16:09
  • @Theia Ya he modificado mi ejemplo, de acuerdo a lo que dices – Ivan Botero Jul 01 '17 at 16:11
  • 1
    @Theia Si presionas una tecla, solo se ejecuta una vez, y si se libera ya vuelve a funcionar. Checalo – Ivan Botero Jul 01 '17 at 16:11
2

Primero que nada keyPress no es igual que KeyDown

KeyPress = KeyDown + KeyUp

   |    KeyDown     ^     KeyUp 
   v                |            KeyPress
|Tecla|          |Tecla|

Para entenderlo mejor véamos qué pasa cuando el usuario no suelta la tecla

   |    KeyDown    |     KeyDown     |    KeyDown    ^     KeyUp 
   v               v                 v               |            KeyPress
|Tecla|         |Tecla|           |Tecla|         |Tecla|

;(function(global)
{
  "use strict"
  function myKeyUp(e)
  {
       console.log("Key up")
  }
  
  function myKeyDown(e)
  {
      console.log("Key down")
  }
  
  function myKeyPress(e)
  {
      console.log("Key Press")
  }
  
  var input = document.getElementsByTagName('input')[0]
  input.onkeyup = myKeyUp
  input.onkeydown = myKeyDown
  input.onkeypress = myKeyPress
}(window))
<input type="text" placeholder="Escribe aquí"></input>

Cabe mencionar que el correcto funcionamento de keyPress depende principalmente del widget toolkit adyacente a él, si el anterior código no funciona, basta hacer el siguiente fix

;(function(global)
{
"use strict"
 var miAPI = new function()
 {
  var keyup = false
  
  this.makeKeyUp = function(fn)
  {
   return function(e){
    var auxEv
    fn.call(null, e)
    keyup = true
    auxEv = new KeyboardEvent('keypress', e)
    e.target.dispatchEvent(auxEv) 
   }
  }

  this.makeKeyDown = function(fn)
  {
   return function(e)
   {
    fn.call(null, e)
    keyup = false
   }
  }

  this.makeKeyPress = function(fn)
  {
   return function(e)
   {
    if (keyup) {
     fn.call(null , e)
    }
   }
  }

 }

  function myKeyUp(e)
  {
       console.log("Key up")
  }
  
  function myKeyDown(e)
  {
      console.log("Key down")
  }
  
  function myKeyPress(e)
  {
      console.log("Key Press")
  }
  
  
  var input = document.getElementsByTagName('input')[0]
  input.onkeyup = miAPI.makeKeyUp(myKeyUp)
  input.onkeydown = miAPI.makeKeyDown(myKeyDown)
  input.onkeypress = miAPI.makeKeyPress(myKeyPress)
}(window))
<input type="text" placeholder="Escribe aquí"></input>
Eduen Sarceño
  • 2,072
  • 10
  • 21
  • cabe mencionar que el anterior ejemplo utiliza una API deprecada, por lo tanto nadie debería usarla, `keydown`, `keypress`, `keyup` son sustituidos por `input` véase la siguiente documentación https://developer.mozilla.org/en-US/docs/Web/Events/input – Eduen Sarceño Jun 30 '17 at 22:35
2

Utiliza la propiedad KeyboardEvent.repeat, así sabrás si estás presionando la misma tecla por mucho tiempo:

input = document.querySelector('input');

input.addEventListener('keypress', e => {
  if(e.repeat){
    e.preventDefault();
    return;
  }
  // Lógica aquí
  console.log('keypress ' + String.fromCharCode(e.which || e.keyCode))
});

input.addEventListener('keyup', e =>{
  console.log('keyup');
})
<input type="text">
Jose Hermosilla Rodrigo
  • 3,805
  • 1
  • 15
  • 24