37

I have this code:

else if (e.keyCode == 32){
      fired = true;

In a keyDown function (I have added the document.addEventListener code). Now it works just fine, and does exactly what I want it to do. But here's the problem: if you hold down the key, it keeps making fired = true over and over again continuously until it is released. I just want it to set fired = true; once, even if the key is held down.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
Chris
  • 371
  • 1
  • 3
  • 3
  • 1
    You could implement event debouncing... – Šime Vidas May 22 '11 at 12:38
  • Of interest, see [this page](http://unixpapa.com/js/key.html) which (among other in-depth details) notes that the `keydown` event does not auto-repeat on non-Windows Gecko browsers. – Phrogz May 22 '11 at 12:46

8 Answers8

62

Edit: It is now fully supported in every browser. except for Internet Explorer

If browser compatibility is not your main concern*, you could try accessing the .repeat property of the KeyboardEvent, as documented here:
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat

By doing something like this in your handler function:

function keyDown (e) {
  if (e.repeat) { return }

  // do stuff here
}

you could avoid the repeated keystrokes.


*: on the MDN site it states that it works in Firefox, and I have successfully used it in Chrome, so the two major browsers should have no problem with it

Isti115
  • 2,418
  • 3
  • 29
  • 35
19
    var fired = false;

    element.onkeydown = function() {

        if(!fired) {
            fired = true;
            // do something
        }
    };

Then Use onkeyup event

    element.onkeyup = function() {
     fired = false;
    };
Vishwanath Dalvi
  • 35,388
  • 41
  • 123
  • 155
  • Where should I put this stuff? In just a new function or something? (Sorry, I'm sort of new to JavaScript) – Chris May 22 '11 at 12:44
  • @Chris Using `addEventListener` (for non-old-IE) is the correct way to register handlers. The point is that the `var fired=false;` should go outside either function, so that it can be shared by both (via the magic of _closures_). – Phrogz May 22 '11 at 12:50
3

Try this :)

You can determine anywhere else in your script if a key is down by simply determining if keydown is true! You can also execute additional script when a key is down by replacing the console.log(); with whatever you want to be down when a key is down.

Please tell me if this can be improved.

var keydown = false;
window.addEventListener('keydown', function() {
  if (!keydown) {
    keydown = true;
    console.log('key down');
  }
  window.addEventListener('keyup', function() {
    keydown = false;
  });
});
www139
  • 4,960
  • 3
  • 31
  • 56
2

Solutions above dont take in account multiple key presses (imagine a game where the user can presse up and left at the same time but both need to be triggered just once). I needed a universal solution for disabling key repeat on multiple keyPress for ALL keys:

// create an array with 222 (number of keycodes) values set to true
var keyEnabledArray = Array(222).fill(true);

document.onkeydown = function(e){
    // disable the key until key release
    if(keyEnabledArray[e.keyCode]) {
        keyEnabledArray[e.keyCode] = false;
    }
};

document.onkeyup = function(e){
    // enable the specific key on keyup
    keyEnabledArray[e.keyCode] = true;
}; 

Check the snippet below:

// create an array with 222 true values
var keyEnabledArray = Array(222).fill(true);

document.onkeydown = function(e){
    // disable the key until key release
    if(keyEnabledArray[e.keyCode]) {
        keyEnabledArray[e.keyCode] = false;
        document.getElementById('console').innerHTML += e.keyCode + '<br>';
    }
    
};

document.onkeyup = function(e){
    keyEnabledArray[e.keyCode] = true;
};
Press a key:
<div id='console'></div>
TOPKAT
  • 6,667
  • 2
  • 44
  • 72
1

Another approach to handle this (suggested by my friend):

  1. Note the timeStamp when everytime the keydown event happens

  2. Calculate the difference between current timeStamp (t2) and the previous timeStamp (t1)

  3. (a) If the difference is less than some predetermined threshold, then it is the second keydown (means we have already executed the code to happen on that event). Hence, we may choose not to execute the code again

    (b) If the difference is greater than the predetermined threshold, then it is the first keydown. Hence, we would execute the code

Vikram
  • 3,996
  • 8
  • 37
  • 58
1

To avoid repeated keydown event, block it after firing and unblock it with keyup. You should avoid variable 'fired' being global, so use code block with ES6+ or closure in older version of JS.

For ES6:

{ let fired = false;    // code block to exclude 'fired' from global scope
    element.addEventListener('keydown', (e) => {
        // only accept key down when was released
        if(!fired) {
            fired = true;
            // check what key pressed...
            if (e.keyCode === 32) {
                // and do what you need with it
            }
        }
    });

    element.addEventListener('keyup', (e) => {
        fired = false;
    });
}

For older version of JS use IIFE:

(function(){
    let fired = false;
    element.addEventListener('keydown', function(e) {
        // only accept key down when was released
        if(!fired) {
            fired = true;
            // check what key pressed...
            if (e.keyCode === 32) {
                // and do what you need with it
            }
        }
    });

    element.addEventListener('keyup', function(e) {
        fired = false;
    });
})();
ChrisK
  • 141
  • 2
  • 6
1

Use keyup event. It is fired when they key is lifted.

alex
  • 479,566
  • 201
  • 878
  • 984
  • Just be aware that if you are trying to trap keys that the browser uses, `keyup` won't let you prevent the default action (in most cases). [Use this site](http://unixpapa.com/js/key.html) for information on triggering order and when browsers trigger their own events. – saluce Aug 24 '12 at 13:13
0

Unlike the solution above from Hungary where you return false if e.repeat is true, my use case required me to do the following to effectively disable the onChange event on a text input/textarea.

function keyDown (e) {
  if(e.repeat) {
    e.preventDefault()
  }

  // do stuff here
}

Larger context of my use case (in JSX):

<textarea 
  ref={formulaInput}
  defaultValue={formulaDefinition.string}
  onDoubleClick={handleLock} 
  onKeyUp={handleUnlock}
  onKeyDown={(e) => {
    if(e.repeat) {
      e.preventDefault()
    }
  }}
  onKeyPress={(e) => {
    if(e.repeat) {
      e.preventDefault()
    }
  }}
  readOnly={!focusLock}
  onChange={handleChange} 
  className={`formula-input ${focusLock ? 'formula-input__locked' : ''}`}
  id='2'
></textarea>