Naeem Shaikh
Naeem Shaikh

Reputation: 15725

KeyDown doesn't continuously fire when pressed and hold

I have this Plunkr,

This contains a div, on which I bind a keydown event. On pressing right or left arrow, the div should start moving.

This works, in all browsers, but when the key is pressed and hold, the first keydown event is fired immediately (div once moves), and it waits for a time gap, and then it continues to move.

So this means the keydown event is once fired, then the browser waits to detect if there is a subsequent keyUp event as well, then after a short time (when there is no keyup), it continues to fires keydown events.

(to see the problem, focus on window, press right arrow and hold, div should move once 5px, then wait, and then again continue to move)

Question: Is there a way around, so I can just keep the key pressed, and div should immediately start moving, without waiting to detect subsequent keyup (once)?

$(function() {
  $(window).keydown(function(e) {
    console.log(e.keyCode)
    if (e.keyCode == 39)
      move(5, 'left', $('.mover'))
    else if (e.keyCode == 37)
      move(-5, 'left', $('.mover'))
  })

})

function move(offset, direction, target) {
  console.log($(target))
  $(target).css(direction, (parseInt($(target).css(direction)) + offset) + 'px')
}
.mover {
  height: 50px;
  width: 50px;
  display: inline-block;
  background: black;
  position: absolute;
  left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class='mover'></div>

Upvotes: 3

Views: 10053

Answers (5)

Balaji
Balaji

Reputation: 11007

pure javascript

<input id="some" type="text">
var input=document.getElementById("some");
input.addEventListener("keydown",function(e){

if (e.which == 40) {
var stop = setInterval(function(){ 

 console.log("ss:");

        }, 60);
        window.addEventListener("keyup", function(){
            //stop the loop
            clearInterval(stop);
            //and remove the keyup listener
            window.removeEventListener("keyup", arguments.callee);
          })


     }


})

Upvotes: 0

Ghis
Ghis

Reputation: 923

For those that might interest, here is another approach to handle bi-directional movement. Ex: up + right keys = "North East" direction :

const FPS       = 60;
const STEP      = 5; 
const UP_KEY    = 90; // Z
const DOWN_KEY  = 83; // S
const LEFT_KEY  = 81; // Q
const RIGHT_KEY = 68; // D

const pressedKeys = [];
let movingTimeout = null;

function handleKeyDown(e) {
  pressedKeys[e.keyCode] = true;
  switch(e.keyCode) {
    case DOWN_KEY:
    case LEFT_KEY:
    case RIGHT_KEY:
    case UP_KEY:
      e.preventDefault();
      startMoving();
  }
}

function handleKeyUp(e) {
  pressedKeys[e.keyCode] = false;
  const shouldStop = !pressedKeys[UP_KEY]
    && !pressedKeys[DOWN_KEY]
    && !pressedKeys[LEFT_KEY]
    && !pressedKeys[RIGHT_KEY]
  ;
  if(shouldStop) {
    stopMoving();
  }
}

function startMoving() {
  if(!movingTimeout){
    loop();
  }
}

function stopMoving() {
  clearTimeout(movingTimeout);
  movingTimeout = null;
}

function loop() {
  const x = pressedKeys[RIGHT_KEY] ? STEP
          : pressedKeys[LEFT_KEY] ? -STEP : 0;
  const y = pressedKeys[DOWN_KEY] ? STEP
          : pressedKeys[UP_KEY] ? -STEP : 0;
  move(x, y);
  movingTimeout = setTimeout(loop, 1000 / FPS);
}

function move(x, y) {
  // Implement you own logic here for positioning / handling collisions
  // Ex: store.dispatch({ type: "MOVE_PLAYER", x, y });
  console.log(`Moving ${x} ${y} !`);
}


window.addEventListener('keydown', e => handleKeyDown(e));
window.addEventListener('keyup', e => handleKeyUp(e));

Hope this helps.

Cheers !

Upvotes: 0

kev.rowe
kev.rowe

Reputation: 136

I would suggest something on a timeout, like this

http://codepen.io/kevrowe/pen/qEgGVO

$(function() {
  var direction,
      movingTimeout = -1;

  $(window).on('keydown', function(e) {    
    if (e.keyCode == 39) {
      direction = 'right';
    } else if (e.keyCode == 37) {
      direction = 'left';
    }  

    startMoving(direction);
  });

  function stopMoving() {
    clearTimeout(movingTimeout);
    movingTimeout = -1;
  }

  function startMoving(direction) {
    if (movingTimeout === -1) {      
      loop(direction);
    }
  }

  function loop(direction) {
    move(direction === 'left' ? -5 : 5, $('.mover'));
    movingTimeout = setTimeout(loop, 10, direction);
  }

  function move(offset, $target) {
    $target.css('left', (parseInt($target.css('left')) + offset) + 'px')
  }

  $(window).on('keyup', function(e) {    
    stopMoving();
  });
})

Upvotes: 6

hobberwickey
hobberwickey

Reputation: 6444

One solution would be to just continuously run the move function in a loop until keyup happens

if (e.keyCode == 39){
  var stop = setInterval(function(){ 
    move(5, 'left', $('.mover'))
  }, 25);

  window.on("keyup", function(){
    //stop the loop
    clearInterval(stop);
    //and remove the keyup listener
    window.off("keyup", arguments.callee);
  })
} else if //etc...

Upvotes: 1

yas
yas

Reputation: 3620

When keydown event occurs on the key you want to track, then store this information in a mapping or a flag somewhere.

When keyup event occurs, then clear the flag for the given key.

Then, on a timer, you could poll the state of the key mapping, and move the object for whatever key direction is being pressed.

Possibly there is a better solution then polling, but I don't know of a way to test if a key is down other than polling.

On keyup, would also need to check if needed to interrupt moving the object.

Upvotes: 2

Related Questions