Keys pressed at the same time - javascript

Can I know the number of keys pressed at the same time in Javascript?
If so, how can I have an array of their keyCode?

You can listen for keydown and keyup events.
var keys = { length: 0 };
document.onkeydown = function(e){
if(!keys[e.keyCode]) {
keys[e.keyCode] = true;
keys.length++;
}
}
document.onkeyup = function(e){
if(keys[e.keyCode]) {
keys[e.keyCode] = false;
keys.length--;
}
}
Then all the keys that are true are the ones that are pressed currently.
Fiddle demo thanks to #Esailija: http://jsfiddle.net/maniator/Gc54D/

This should do the trick. It is similar to Neal's, but should fix a few issues, including the leaving-the-window bug and the negative-numbers-of-keys bug. I also streamlined the message writing code a bit. I replaced the timer loop for writing the number of keys message with an on-demand system, added a safety mechanism for decrementing the length index, and added the clearKeys to switch all keys to up when the user leaves the window. The code still has two bugs: it will not recognize keys that are still held down after a new window has been opened and closed (they must be released and re-pushed), and I can't get it to recognize more then six keys (I suspect this isn't related to this code, but the computer/browser...).
var keys = {
length: 0
};
window.onkeydown = function(e) {
if (!keys[e.keyCode]) {
keys[e.keyCode] = true;
keys.length++;
document.body.innerHTML = "You are pressing " + keys.length + " keys at the same time.";
}
}
window.onkeyup = function(e) {
if (keys[e.keyCode]) {
keys[e.keyCode] = false;
if (keys.length) {
keys.length--;
}
document.body.innerHTML = "You are pressing " + keys.length + " keys at the same time.";
}
}
function clearKeys() {
for (n in keys) {
n = false
};
keys.length = 0;
document.body.innerHTML = "You are pressing " + 0 + " keys at the same time.";
}
document.body.innerHTML = "You are pressing 0 keys at the same time.";
window.onblur = clearKeys;

Since you want the number of keys pressed at the same time and an array of their key codes I suggest you use the following function:
var getKeys = function () {
var keys = [];
window.addEventListener("blur", blur, false);
window.addEventListener("keyup", keyup, false);
window.addEventListener("keydown", keydown, false);
return function () {
return keys.slice(0);
};
function blur() {
keys.length = 0;
}
function keyup(event) {
var index = keys.indexOf(event.keyCode);
if (index >= 0) keys.splice(index, 1);
}
function keydown(event) {
var keyCode = event.keyCode;
if (keys.indexOf(keyCode) < 0)
keys.push(keyCode);
}
}();
When you call getKeys it will return an array of all the keys pressed at the same time. You can use the length property of that array to find the number of keys pressed at the same time. Since it uses addEventListener it works cooperatively with other code on the page as well.
I tested the above function and it always returns the correct keys pressed even when you switch to another window while holding down a key (it removes that key from the array). If you hold a key and switch back then it recognizes that a key is pressed and it pushes in onto the array. Hence I can testify that there are no bugs in the above code. At least not on the browser I tested it on (Opera 12.00).
I was able to press 8 keys at the same time (A, S, D, F, J, K, L and ;). This number seems OS specific as I can only press 4 left hand keys and 4 right hand keys at the same time. For example after I press A, S, D and F, and then I press another left hand (let's say G) then it won't recognize the last key. This is probably because the OS knows how humans type and so it only allows four interrupts for each of the left and right hand keys. The OS I am using is Ubuntu 12.04.
You can see the code in action on this fiddle. I used a Delta Timer instead of setInterval to display the results after every 50 ms. You may also wish to read the following answer as well.

Related

How to fix this if statement

I have an object which I want to move diagonally when pressing two keys. So far it works, but after releasing the two keys, if I press just one key, namely 's', it keeps moving diagonally. How can I get the object move diagonally ONLY when pressing the two keys together?
let keysPressed = ["d", "s"];
const box1 = document.getElementById("box1");
var box1x = box1.offsetLeft;
var box1y = box1.offsetHeight;
document.addEventListener('keydown', (event) => {
keysPressed[event.key] = true;
if (keysPressed['d'] && event.key == 's') {
box1.style.left = document.getElementById("box1").offsetLeft + 5 + 'px';
box1.style.top = document.getElementById("box1").offsetTop + 5 + 'px';
}
});
You should add handling for key up events. Now the values of keyPressed is never cleared.
After first press, the keysPressed still keep value true of previous key. So after that, the condition keysPressed['d'] always true and that cause to your currently issue.
You should update it to false in the end of the callback function.
keysPressed[event.key] = true;
// ....
keysPressed[event.key] = false;

Detect and print key combinations Javascript

I'm currently trying to convert user-provided keyboard combinations into their printable versions (so for example if a user presses shift + a I want to be able to detect it and print the string "shift + a" somewhere so that the user knows which combination he associated with the action.
So far I've been using the provided booleans: ctrlKey, altKey, shiftKey and metaKey, and when they're true I add their printable versions to the final string.
It works... to some extent...
I correctly receive the ctrl flag, but the shift flag is erratic (giving shifts when it should not and not giving shifts when it should, although it works OK for some keys) and the alt flag seems to work only on a few keys and the meta flag does not seem to work at all.
Note that my keyboard works properly, that is, these keys work as intended in normal conditions (the shift key allows me to capitalize, the win key allows me to lock my computer, etc...)
Also note that my keyboard is an azerty.
It behaves the same way on the W3schools example:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_event_key_shiftkey
Here is an example:
trying with &, which does not require shift on an azerty
trying with 1, which does require shift on an azerty
So I assume there's something I'm misunderstanding in the way to detect key combinations. Thanks to all those who'll be willing to help.
function logKey(e) {
var modifier1;
var modifier2;
var modifier3;
var modifier4;
var display = "";
if (e.ctrlKey) {
modifier1 = "ctrl + ";
}
else {
modifier1 = "";
}
if (e.altKey) {
modifier2 = "alt + ";
}
else {
modifier2 = "";
}
if (e.shiftKey) {
modifier3 = "shift + ";
}
else {
modifier3 = "";
}
if (e.metaKey) {
modifier4 = "meta + ";
}
else {
modifier4 = "";
}
display = modifier1 + modifier2 + modifier3 + modifier4 + e.key
window.alert(display);
}
window.addEventListener('keydown', logKey);
As skyline3000 hinted in their comment, you are probably better off independently tracking which keys are pressed and then when you need them, simply look at what you've tracked.
Something like this:
const input = document.querySelector('input');
const keysDown = [];
input.addEventListener('keydown', e => {
if (keysDown.indexOf(e.keyCode) === -1) {
keysDown.push(e.keyCode);
}
console.log(keysDown);
});
input.addEventListener('keyup', e => {
const index = keysDown.indexOf(e.keyCode);
if (index !== -1) {
keysDown.splice(index, 1);
}
console.log(keysDown, e);
});
Focus on input to register keys:
<input />
This will let you track them way more precisely and can even track lots of keys down at once.
Then, when you want to show which keys they pressed, you just print out your keysDown list.
So...
I went back to the problem today, and realized that the issue is not even limited to these booleans. Even if I ignore the booleans and try to use keycodes to register modifier keys being pressed, that does not work (using #samanime code), at least not on Firefox, because apparently my firefox does not trigger a keydown/keyup (or keypress) event when I press the modifier keys like ctrl or alt. It does trigger normally on other keys like the character keys for example.
It works on Chrome though.
Is there some compatibility problem that I'm not aware of? Or is it my Firefox that has an issue somehow?
Very simple solution:
let keys = {};
let keysPressed = '';
document.onkeydown = handleKey;
document.onkeyup = handleKey;
function handleKey(e) {
// Indicate key pressed
if (e.type == 'keydown') {
keys[e.key] = true;
}
else {
keys[e.key] = false;
}
// Run on all keys and determine which are pressed
keysPressed = '';
for (const [key, value] of Object.entries(keys)) {
if (value) {
keysPressed += key;
}
}
// Show key presses
document.querySelector('.keys').innerHTML = keysPressed;
}
<div class="keys">Press some keys</div>

Detect alternating key events

I'm writing a script for the following task:
The task is for participants to alternately press the a and b keys on the keyboard as quickly as possible for 10 minutes. Every time a participant successfully press the a key followed by the b key, they should receive a point. Points should only be awarded for alternating key presses, pressing the a key or the b key without alternating between the two should not result in points.
The part of the problem I am asking about is the detection of alternating key events. I attempted this myself and ended up with the code below, but it does not achieve the desired result and I'm getting the following error:
Uncaught ReferenceError: x is not defined
... but I just don't understand what I'm doing wrong.
How can I fix my code and achieve the desired result?
var points = 0;
document.addEventListener('keydown', function(event) {
var x = event.code;
});
document.addEventListener('keydown', function(event) {
if (x == 'KeyA' && event.code == 'KeyB') {
points = points + 1;
document.getElementById("points").innerHTML = points;
}
});
<p>Points: <span id="points">0</span></p>
You only need one event listener, and you need to define the a key press state variable (x) outside of the listener function so that it can be referenced by subsequent executions of the listener function.
You also need to make sure that you reset the a key press variable after the b key press.
it is also generally a good idea to cache your references to elements, rather than selecting the element from the DOM each time your listener function runs, and using textContent instead of innerHTML bypasses the HTML parser.
const target = document.getElementById('points');
var points = 0, x;
document.addEventListener('keydown', function(event) {
if(event.key === 'a') x = true; // If this is an `a` key event, set x to true
if(event.key === 'b') {
// if this is a `b` key event and a was not pressed, return early
if(!x) return;
// otherwise increment the points variable and assign the result to
// the textContent property of the target element
target.textContent = ++points;
// remember to set x to false again
x = false;
}
});
<p>Points: <span id="points">0</span></p>

press two continuous key to trigger an event in JavaScript

In Vim, you can press gg to go to the beginning of the document, Or press dd go delete the current line. How to implement similar behavior in a web page? I mean, in a web page environment, how can I capture two continuous key press event to trigger an event?
Thanks.
You would need to monitor all key press events and when you find a key that is possibly the first in a multi-press combo, start a timer. If the second key in the combo is pressed while the timer is active, do something.
eg (pseudocode)
//for gg
var inCombo = false;
function KeyPress(Key) {
if(Key=='g') {
if(!inCombo) {
inCombo = true;
setTimeout('inCombo=false;', 100);
} else {
//Do the action here
}
}
}
The //Do the action here, will only fire if g is pressed twice within 100ms
You can't. Simply register the normal key event and push the keys to an array.
Now you can call a function which checks for the commands:
// More or less pseudo code
function deleteLine(){};
function copyLine(){};
var commands = {'dd': deleteLine, 'yy': copyLine};
function onKeyPress(e) {
keyList.push(e.key);
// in this example keyList = ['d', 'y', 'i', 'd', 'd']
var result = handleEvent();
}
function handleEvent(keyList) {
// more pseudo code follows
var cmds = commands.keyValue.sortByLengthDescending();
for(var c in cmds) {
// match the keys
var ckey = c.split('');
for(var i = keyList.length; i >= 0; i--) {
if (keyList[i] !== ckey.shift()) {
break;
}
if (ckey.length === 0) {
return commands[c]();
}
}
}
}
This is simple, clean (depends on how exactly you write it) and scalable, adding more commands is pretty easy, of course you change it so that you can pass parameters to the command function etc.

Doing key combos with jQuery / JavaScript

I'm curious how i, with the following jQuery plugin code im writing at the bottom of this question, could implement key combos. How it's working so far is it allows a user to create key commands simply by doing a normal jQuery like syntax and provide an event for the key command, like so:
$(window).jkey('a',function(){
alert('you pressed the a key!');
});
or
$(window).jkey('b c d',function(){
alert('you pressed either the b, c, or d key!');
});
and lastly what i want is the ability to do, but can't figure out:
$(window).jkey('alt+n',function(){
alert('you pressed alt+n!');
});
I know how to do this outside of the plugin (on keyup set a var false and on keydown set the var true and check if the var is true when you press the other key), but i don't know how to do this when you dont know what keys are going to be pressed and how many. How do I add this support? I want to be able to allow them to do things like alt+shift+a or a+s+d+f if they wanted. I just can't get my head around how to implement this. Any ideas?
I'm going to release this as an open source plugin and i'd love to give whoever gives me the right, working, answer some credit on the blog post and in the code it's self. Thanks in advance!
(function($) {
$.fn.jkey = function(keyCombo,callback) {
if(keyCombo.indexOf(' ') > -1){ //If multiple keys are selected
var keySplit = keyCombo.split(' ');
}
else{ //Else just store this single key
var keySplit = [keyCombo];
}
for(x in keySplit){ //For each key in the array...
if(keySplit[x].indexOf('+') > -1){
//Key selection by user is a key combo... what now?
}
else{
//Otherwise, it's just a normal, single key command
}
switch(keySplit[x]){
case 'a':
keySplit[x] = 65;
break;
case 'b':
keySplit[x] = 66;
break;
case 'c':
keySplit[x] = 67;
break;
//And so on for all the rest of the keys
}
}
return this.each(function() {
$this = $(this);
$this.keydown(function(e){
if($.inArray(e.keyCode, keySplit) > -1){ //If the key the user pressed is matched with any key the developer set a key code with...
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
}
});
});
}
})(jQuery);
Use keypress instead of keyup/keydown because the latter two do not accurately protray the keycode (reference, see last paragraph). You can reference the altKey ctrlKey and shiftKey boolean properties of the event object in this case...
$(document).keypress(function(e) {
var key = String.fromCharCode(e.which);
var alt = e.altKey;
var ctrl = e.ctrlKey
var shift = e.shiftKey;
alert("Key:" + key + "\nAlt:" + alt + "\nCtrl:" + ctrl + "\nShift:" + shift);
});
Also, you can use String.fromCharCode to translate the key code to an actual letter.
You can't trap multiple keys aside from combinations with Ctrl, Alt, and Shift. You simply can't do it in a single event. So throw the a+s+d+f idea out the window.
Note: Obviously there are certain key combinations that are used by the browser. For instance, Alt + F usually brings up the File menu in Windows. Ctrl + N usually launches a new window/tab. Do not attempt to override any of these combinations.
Here's a live demo for your testing pleasure.
Here's what I came up with. Essentially what I did was created a JSON object that stores all the key codes. I then replace all the provided keys with the codes. If the keys are using the '+' to make a key combo, I then create an array of the codes out of it.
We then create another array that stores all the keys that are being pressed (keyDown add the item, keyUp removes it). On keyDown, we check if it's a single key command or combo. If it's a combo, we check it against all the currently active key presses. If they all match, we execute the callback.
This will work with any number of key combos. Only time I saw that it wasn't working is when you use the 'alert()' to display a message on the key combo because it will no longer remove the items from the active key press array.
(function($) {
$.fn.jkey = function(keyCombo,callback) {
// Save the key codes to JSON object
var keyCodes = {
'a' : 65,
'b' : 66,
'c' : 67,
'alt' : 18
};
var x = '';
var y = '';
if(keyCombo.indexOf(' ') > -1){ //If multiple keys are selected
var keySplit = keyCombo.split(' ');
}
else{ //Else just store this single key
var keySplit = [keyCombo];
}
for(x in keySplit){ //For each key in the array...
if(keySplit[x].indexOf('+') > -1){
//Key selection by user is a key combo
// Create a combo array and split the key combo
var combo = Array();
var comboSplit = keySplit[x].split('+');
// Save the key codes for each element in the key combo
for(y in comboSplit){
combo[y] = keyCodes[ comboSplit[y] ];
}
keySplit[x] = combo;
} else {
//Otherwise, it's just a normal, single key command
keySplit[x] = keyCodes[ keySplit[x] ];
}
}
return this.each(function() {
$this = $(this);
// Create active keys array
// This array will store all the keys that are currently being pressed
var activeKeys = Array();
$this.keydown(function(e){
// Save the current key press
activeKeys[ e.keyCode ] = e.keyCode;
if($.inArray(e.keyCode, keySplit) > -1){ // If the key the user pressed is matched with any key the developer set a key code with...
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
} else { // Else, the key did not match which means it's either a key combo or just dosn't exist
// Check if the individual items in the key combo match what was pressed
for(x in keySplit){
if($.inArray(e.keyCode, keySplit[x]) > -1){
// Initiate the active variable
var active = 'unchecked';
// All the individual keys in the combo with the keys that are currently being pressed
for(y in keySplit[x]) {
if(active != false) {
if($.inArray(keySplit[x][y], activeKeys) > -1){
active = true;
} else {
active = false;
}
}
}
// If all the keys in the combo are being pressed, active will equal true
if(active === true){
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
}
}
}
} // end of if in array
}).keyup(function(e) {
// Remove the current key press
activeKeys[ e.keyCode ] = '';
});
});
}
})(jQuery);
This is just a shot in the dark but maybe it'll help you down the right path.
If it's possible to have that function recognize a hexadecimal value for the key that you entered instead of the literal key (such as 0x6E for the letter 'n'), you could derive what "alt+n" translates to in hex and have the function look out for that value.
If you're looking for something that will let a user easily enter and define key combos using a plain input box, I wrote a plugin that does it for you: http://suan.github.com/jquery-keycombinator/

Categories