views:

311

answers:

4

In Javascript, how do I tell if a user is pressing two keys at the same time?

For example, I have drawn a circle in the middle of the screen. I want to move it up while the user holds down the up arrow and right while the user holds down the right arrow. That part works easy. If user holds both the up and right arrows, I want to move the circle diagonally, up and to the right.

It doesn't look like this possible with basic Javascript event handling, but surely someone has figured out a work around/hack/improvement.

+1  A: 

Javascript keyboard events fire on keypress and keydown, but don't contain the additional keymask info to determine if other keys are pressed at the same time.

John Weldon
+6  A: 

Here is what you need to do conceptually (I guess this is called pseudo code):

Start with something like this:

var PIXEL_DELTA  = 10; // Distance to move in pixels

var leftPressed  = 0,
    upPressed    = 0,
    downPressed  = 0,
    rightPressed = 0;

Then on every keydown event, test if it the key pressed is left, up, etc and turn its variable from 0 to PIXEL_DELTA.

On every keyup event, run the same test and turn the correct variable back to 0.

Then, in your moving code (real code): (This code adapted from Crescent Fresh's awesome example):

function move() {
  var dot = document.getElementById('dot'),
      deltaX = rightPressed - leftPressed,
      deltaY = downPressed - upPressed;

  if(deltaX) {
    dot.style.left = (parseInt(dot.style.left, 10) || 0) + deltaX + 'px';
  }

  if (deltaY) {
    dot.style.top = (parseInt(dot.style.top, 10) || 0) + deltaY + 'px';
  }
}

The browser will (should) fire a separate keydown/keyup event for each key, even if they are "simultaneously" pressed.

Working Example

Crescent Fresh put together a full example on JSBin. Be sure to visit the editable version as well to play with the code.

Doug Neiner
Yes, but I'd use numbers and toggle between `0` (your false) and `n` (your true), where `n` is the pixel delta. The moving code wouldn't need to test booleans then, but just do some basic arithmetic. Non-psuedo code: http://jsbin.com/iloxi
Crescent Fresh
@Crescent, I love it. I'll update my answer if you don't want to post your own, but you really should since its a great solution and you have a working demo.
Doug Neiner
@DN: be my guest, you nailed it.
Crescent Fresh
Done, thanks man!
Doug Neiner
+2  A: 

Maybe you can do it by keeping track of keydown and keyup event for each key and you'll know if two keys are being held at the same time.

Sample pseudo-code:

var keydown = {};

function onkeydown(event) {
   keydown[event.key] = true;
}

function onkeyup(event) {
   keydown[event.key] = false;
}

// in some function at some other places

if (keydown['up'] && keydown['right']) {
  move_diagonal('up', 'right');
}
elseif (keydown['up'] && keydown['left']) {
  move_diagonal('up', 'left');
}
elseif .. blah blah
Lukman
+4  A: 

Javascript has onkeydown, and onkeyup events. You can simple fiddle a bit onkeydown for left arrow, and fiddle another bit for the up arrow. On keyup, fiddle the respective bit back.

var leftPressed,
    upPressed,
    rightPressed,
    downPressed;

var milli = 100;

window.addEventListener('onkeydown', function(e) {
    switch(e.keycode) {
        case 37: //left
            leftPressed = true;
        case 38: //up
            upPressed = true;
        case 39: //right
           rightPressed = true;
        case 40: //down
            downPressed = true;
        default:
            break;
    }
});

window.addEventListener('onkeyup', function(e) {
    switch(e.keycode) {
        case 37: //left
            leftPressed = false;
        case 38: //up
            upPressed = false;
        case 39: //right
           rightPressed = false;
        case 40: //down
            downPressed = false;
        default:
            break;
    }

});

function moveCircle() {
    if(leftPressed && !rightPressed) {
        // move left
    }
    if(rightPressed && !leftPressed) {
        // move right
    }
    if(upPressed && !downPressed) {
        // move up
    }
    if(downPressed && !upPressed) {
        // move down
    }
}

setInterval(moveCircle, milli);
seanmonstar
I think the general idea you have will work, but I don't think this code does. The key down message is sent repeatedly if a key is held down. And I don't want to move the circle back on keyup. I want to stop moving it.
BlueWaldo
Your're right, I might have mixed up onkeydown and onkeypress. However, the onkeyup part is to stop moving in the direction. On left down, dx becomes -1. On lifting it, dx +1 becomes 0 again.
seanmonstar
@seanmonstar: You still have a problem with the keydown event autorepeating though.
Tim Down
ok, swapped it out for onkeypress, which fires only once.
seanmonstar
It doesn't look like Chrome/Safari/IE fire the keypress event for arrow keys.
BlueWaldo
`keydown` was the right event, as @BlueWaldo noted. I think you might be better off just storing the up/down state of the keys you're interested in as booleans.
Tim Down
Indeed, changed it back to `onkeydown`, and instead of increasing dx, just set a boolean that a key has been pressed and not released yet.
seanmonstar