13

I have a same problem like this guy How to disable repetitive keydown in jQuery , it's just that I'm not using jQuery so I'm having hard time translating it to "pure" JavaScript, anyways I have set switch-case of some keys and when I press and hold right arrow key my div is flying. Also if it's not a problem can you tell me what would be the easiest way to stop div movement when I let the right arrow key go, do I have to make a new switch case with clearInterval or?

switch (keyPressed) {
    case 39:
        setInterval(movFwr, 50);
        break;
}

function movFwr() {
    if (currPos == 1000) {
        a.style.left = 1000 + "px";
    } else currPos += 10;
    a.style.left = trenutnaPozicija + "px";
}

I'm sorry guys, I've been busy a bit, I'm testing all possibilities and so far I have seen some interesting suggestions. I'll test em all these days and then rate of what ever is that you do on this site. Great community I must say. Thank you all for your help :)

Community
  • 1
  • 1
Novak
  • 145
  • 1
  • 1
  • 5

4 Answers4

22

Something like this should do the trick;

var down = false;
document.addEventListener('keydown', function () {
    if(down) return;
    down = true;

    // your magic code here
}, false);

document.addEventListener('keyup', function () {
    down = false;
}, false);
yckart
  • 32,460
  • 9
  • 122
  • 129
  • 1
    This is the simplest, most universal solution. – allen1 Oct 14 '16 at 03:12
  • 5
    Why create and manage another variable when there is a "repeat" boolean property on the event, which is set to true once the event starts to repeat. document.addEventListener('keydown', function(e) { if (e.repeat) return; // TODO... }, false); – DDN-Shep Apr 10 '19 at 14:22
  • @DDN-Shep Well, `Status Obsolete`... https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat – yckart Apr 13 '19 at 12:22
  • Still relevant in 2019, but it's definitely better to, rather than `down = false` to use `const isDown = {}` and then setting/checking `isDown[evt.key]` as boolean instead (also, certainly today, but even in 2013, the capture flag was mostly useless, so no need to specify it) – Mike 'Pomax' Kamermans Oct 08 '19 at 00:00
  • The status is obsolete, but where is the replacement? MDN says that DOM3 spec for KeyBoardEvent itself is obsolete, and that the draft spec at https://w3c.github.io/uievents/#events-keyboardevents is the current one. But that includes repeat. So honestly, its a bit confusing. – Jacob Lee Aug 11 '20 at 15:53
  • This code doesn’t always work as it should be: if a key is held and another key is pressed at the same time, it will only take account of the first key. For example, if you make a game where you control your character with the four arrow keys, the diagonal will be impossible. – BarryCap Apr 04 '23 at 15:31
  • @DDN-Shep Could you post that as an answer? I can’t find a good example with the repeat property on the Internet. – BarryCap Apr 04 '23 at 15:35
2

I would record the time and prevent action unless enough time has passed, for example using Date.now

var lastPress = 0;

function myListener() {
    var now = Date.now();
    if (now - lastPress < 1000) return; // less than a second ago, stop
    lastPress = now;
    // continue..
}

This can be made into a more generic function

function restrict(func, minDuration) {
    var lastPress = 0;
    return function () {
        var now = Date.now();
        if (now - lastPress < minDuration) return; // or `throw`
        lastPress = now;
        return func.apply(this, arguments);
    };
}
// now, for example
foo = function () {console.log('foo');};
bar = restrict(foo, 200); // only permit up to once every 200ms
bar(); // logs "foo"
bar(); // void as less than 200ms passed
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • This could possibly block different keys from being pressed if they are pressed during the 1 second in the first code, or quicker then the minDuration in the second. – Patrick Evans Jul 07 '13 at 18:26
  • True, depending on where this break in the code is employed, for example if it's all keys or only after you've already established which key. – Paul S. Jul 07 '13 at 20:22
  • This one helped me with 1 other problem I had (had to modify it a bit) :D Thank you. – Novak Jul 07 '13 at 23:27
1

To ignore key events caused by key repeats, keep track of which keys are pressed during the keydown and keyup events.

var pressedKeys = [];
function keydownHandler(e) {
    var isRepeating = !!pressedKeys[e.keyCode];
    pressedKeys[e.keyCode] = true;
    switch (e.keyCode) {
        case !isRepeating && 39:
            // do something when the right arrow key is pressed, but not repeating
            break;
    }
}
function keyupHandler(e) {
    pressedkeys[e.keyCode] = false;
}

To stop your div moving, you could keep track of the interval ids in an array, and clear the interval during the keyup event with something like clearInterval(intervalIds[e.keyCode]), but I'd probably switch to using setTimeout() and checking whether they key is down instead. That way, you don't have to keep track of another variable.

var pressedKeys = [];
function keydownHandler(e) {
    var isRepeating = !!pressedKeys[e.keyCode];
    pressedKeys[e.keyCode] = true;
    switch (e.keyCode) {
        case !isRepeating && 39:
            movFwr();
            break;
    }
}
function keyupHandler(e) {
    pressedkeys[e.keyCode] = false;
}
function movFwr() {
    if (pressedKeys[39] && currPos < 1000) {
        currPos += 10;
        a.style.left = currPos + "px";
        setTimeout(movFwr, 50);
    }
}

This way, you also automatically stop repeating the function as soon as the div reaches the right edge, instead of waiting for the user to release the arrow key.

gilly3
  • 87,962
  • 25
  • 144
  • 176
0

Track the last key pressed, if its the same as current ignore it by returning, and use clearInterval to stop the intervals

//global variables
var lastKey = 0;
var moveTimer = [];

//in keydown function
if(lastKey == keyPressed)
    return;
switch (keyPressed) {
    case 39:
        lastKey = keyPressed
        moveTimer[keyPressed] = setInterval(movFwr, 50);
        break;
}


//in a onkey up function
lastKey = null;
if(typeof(moveTimer[keyPressed]) != "undefined")
    clearInterval(moveTimer[keyPressed]);
Patrick Evans
  • 41,991
  • 6
  • 74
  • 87