49

Is there a way for jQuery to detect that more than one key was pressed at the same time?

Is there any alternative that allows for pressing two keys at the same time to be detected?

Chris Abrams
  • 39,732
  • 19
  • 51
  • 57

8 Answers8

78

In order to detect multiple keys being held down, use the keydown and keyup events.

var keys = {};

$(document).keydown(function (e) {
    keys[e.which] = true;
});

$(document).keyup(function (e) {
    delete keys[e.which];
});

I've put together a demo here: http://jsfiddle.net/gFcuU/. It's kind of fun, though I noticed my keyboard is only able to detect at most 6 keys.

David Tang
  • 92,262
  • 30
  • 167
  • 149
  • 1
    Nice script, I was able to get only 6 keys at the same time max when their char codes were far apart (2 or more difference). ps. why delete the key and not set it just to false? – jerone Feb 10 '11 at 07:46
  • @jerone I guess I didn't want the object to clutter up with a history of all the keys, and it'll save a few iterations in the `printKeys` loop. But in practice, you're right, it's a small difference. – David Tang Feb 10 '11 at 07:51
  • I can also do around 7 keys, however for what I want (arrows keys) there are some combinations that either allows 3 or 2, but never 4... interesting – ajax333221 May 20 '12 at 04:45
  • @ajax333221 this is usually a hardware problem - due to how your keyboard is wired up. Different keyboards will behave differently. – David Tang May 26 '12 at 10:43
  • Box9, how about holding two keys down and have the two keys increment an x and y? this works for just pressing the keys how about holding them down? Thank you for giving an elegant answer to this question, btw. – Emanegux Nov 24 '12 at 02:48
  • 2
    See: http://jsfiddle.net/gFcuU/524/ - try to CTRL + C some text. Afterwards, the "keys down" will stay at 1, until you press/release CTRL and C, seperately. How can we avoid this? – FooBar Nov 10 '13 at 16:30
  • late to the game, but you can't only detect 6 keys at once because you're keyboard is USB. if you had a ps2 keyboard they have what's called n key rollover which allows n key presses to be detected at once. – wootscootinboogie May 15 '14 at 17:46
  • Some keyboards now offer n-key rollover on USB. For example, WASDKeyboards and Jeff Atwood's CODE keyboard. – ivanjonas Dec 24 '14 at 18:26
  • I guess the limit is because of filling the keyboard buffer, on keyboard with antighosting I can press all the keys at once and it catches all the keystrokes correctly. –  Jun 01 '15 at 05:16
  • I'm on Mac, and when trying your demo with any CMD+[key] that is registered as operating system shortcut (for ex. CMD+C) it will not detect the keyup for the second key. Is there any way around it? – Lulu Jul 14 '16 at 11:22
60

It depends. For "normal" keys, that means Non- Shift, Ctrl, ALT, (CMD), the answer is no, the event handler will catch/fire in a queue, one after another.

For the modifier keys I mentioned above, there is a property on the event object.

Example:

$(document).bind('keypress', function(event) {
    if( event.which === 65 && event.shiftKey ) {
        alert('you pressed SHIFT+A');
    }
});

Jsfiddle demo.

Other propertys are:

  • event.ctrlKey
  • event.altKey
  • event.metaKey
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
jAndy
  • 231,737
  • 57
  • 305
  • 359
3

If you just want to fire a handler when several keys are pressed in series, try something like:

jQuery.multipress = function (keys, handler) {
    'use strict';

    if (keys.length === 0) {
        return;
    }

    var down = {};
    jQuery(document).keydown(function (event) {
        down[event.keyCode] = true;
    }).keyup(function (event) {
        // Copy keys array, build array of pressed keys
        var remaining = keys.slice(0),
            pressed = Object.keys(down).map(function (num) { return parseInt(num, 10); }),
            indexOfKey;
        // Remove pressedKeys from remainingKeys
        jQuery.each(pressed, function (i, key) {
            if (down[key] === true) {
                down[key] = false;
                indexOfKey = remaining.indexOf(key);
                if (indexOfKey > -1) {
                    remaining.splice(indexOfKey, 1);
                }
            }
        });
        // If we hit all the keys, fire off handler
        if (remaining.length === 0) {
            handler(event);
        }
    });
};

For instance, to fire on s-t,

jQuery.multipress([83, 84], function () { alert('You pressed s-t'); })
  • There are definitely better solutions out there, but if you're just adding key presses for triggering debug code, etc, it's nice not to add another dependency. – Robert Alex Matevish Jun 17 '16 at 17:50
2

Here's a jQuery solution based on Maciej's answer https://stackoverflow.com/a/21522329/

// the array to add pressed keys to
var keys = [];
// listen for which key is pressed
document.addEventListener('keydown', (event) => {
    if ($.inArray(event.keyCode, keys) == -1) {
        keys.push(event.keyCode);
    }
    console.log('keys array after pressed = ' + keys);
});
// listen for which key is unpressed
document.addEventListener('keyup', (event) => {
    // the key to remove
    var removeKey = event.keyCode;
  // remove it
    keys = $.grep(keys, function(value) {
        return value != removeKey;
    });
    console.log('keys array after unpress = ' + keys);
});
// assign key number to a recognizable value name
var w = 87;
var d = 68;
var s = 83;
var a = 65;
// determine which keys are pressed
document.addEventListener('keydown', (event) => {
  if ($.inArray(w, keys) != -1 && $.inArray(d, keys) != -1) { // w + d
    console.log('function for w + d combo');
  } else if ($.inArray(s, keys) != -1 && $.inArray(a, keys) != -1) { // s + a
    console.log('function for s + a combo');
  }
})

fiddle demo

https://jsfiddle.net/Hastig/us00zdo6/

Hastig Zusammenstellen
  • 4,286
  • 3
  • 32
  • 45
2

Nope. keypress will fire for every individual key that is pressed - except for modifier keys such as CTRL, ALT and SHIFT, you can combine them with other keys, so long as it is only one other key.

Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
1

If you're using esma6, you could do the following using sets.

const KEYS = new Set();

$(document).keydown(function (e) {
   KEYS.add(e.which);
   if(KEYS.has(12) && KEYS.has(31)){
      //do something
   }
});
$(document).keyup(function (e) {
   KEYS.delete(e.which);
});

And if you want the user to press them together, you can do:

const KEYS = new Set(); // for other purposes
const RECENT_KEYS = new Set(); // the recently pressed keys
const KEY_TIMELAPSE = 100 // the miliseconds of difference between keys

$(document).keydown(function (e) {
   KEYS.add(e.which);
   RECENT_KEYS.add(e.which);
   setTimeout(()=>{
      RECENT_KEYS.delete(e.which);
   }, KEY_TIMELAPSE);
   if(RECENT_KEYS.has(37) && RECENT_KEYS.has(38)){
      // Do something
   }
});
$(document).keyup(function (e) {
   KEYS.delete(e.which);
   RECENT_KEYS.delete(e.which);
});

Here is a codepen https://codepen.io/Programador-Anonimo/pen/NoEeKM?editors=0010

0

As my gist expired ( no one was using it :( ) I decided to update the answer with more 2017 solution. Check below.

You can use my plugin for jquery to detect shortcuts.

It basically cache's events and get what keys are pressed at the moment. If all the keys are pressed it fires function.

https://github.com/maciekpaprocki/bindShortcut (expired!)

You have small explanation how to use it in readme file. Hope this helps. Feedback more than appreciated.

Edit 2017:

It's 2017 and we don't need jQuery plugins to solve stuff like that. In short you will need something like this:

let pressed = {};

document.addEventListener('keydown', (event) => {
   pressed[event.key] = true;
});
document.addEventListener('keyup', (event) => {
   delete pressed[event.key];
});

//and now write your code
document.addEventListener('keydown', (event) => {
  if(pressed[firstKey]&&pressed[secondKey]){
    //dosomething
  }
});

Older browsers might have some quirks, however from IE9 everything should work fine except of marginal amounts of OSs that don't support right event delegation (super old ubuntu etc.). There's no way to fix that in them as that's not the browser issue.

There are some quirks in new macs connected to boolean keys like for example caps lock.

Read more: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names_and_Char_values

Maciej Paprocki
  • 1,230
  • 20
  • 29
  • It would be great to see some code. This broken link renders the answer obsolete. – Dan-Levi Tømta Nov 08 '17 at 18:54
  • Unfortunately I see that my gist was removed. Either way. jQuery plugin for something like this in 2017 is just a joke, so I will update the answer with solution soon. If not downvote me till I do :) – Maciej Paprocki Nov 10 '17 at 15:04
0

According to @David Tang's solution, here is a quick and dirty customization for capturing Shift+Ctrl+A combination:

var pressedKeys = {};

function checkPressedKeys() {
    var shiftPressed=false, ctrlPressed=false, aPressed=false;
    for (var i in pressedKeys) {
        if (!pressedKeys.hasOwnProperty(i)) continue;
        if(i==16){
            shiftPressed=true;
        }
        else if(i==17){
            ctrlPressed=true;
        }
        else if(i==65){
            aPressed=true;
        }
    }
    if(shiftPressed && ctrlPressed && aPressed){
        //do whatever you want here.
    }
}


$(document).ready(function(){
    $(document).keydown(function (e) {
        pressedKeys[e.which] = true;
        checkPressedKeys();
    });

    $(document).keyup(function (e) {
        delete pressedKeys[e.which];
    });
});
yılmaz
  • 1,818
  • 13
  • 15