Doing key combos with jQuery / JavaScript - 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/

Related

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>

How to obtain full value inside jQuery's keypress event?

I'm writing some jQuery which intercepts the value entered in an input field and determines whether to allow or to prevent the typed value.
I need to get the next value, i.e., what the value will be were I to permit the key-press, and I need to know this value at a point before it is displayed, so I'm using the keypress event.
My question is: inside the keypress event, how can I tell what the resultant value would be were I to permit the key-press? What is the 'potential value'?
If I write out the key-press event object to the console and inspect the properties, I can see that currentTarget.value shows this 'potential value'. However, if I use this property inside the keypress event then it returns only the value prior to the context key-press.
For example, if the user types "a" into an empty text box bound to the following jQuery
$(":input").on("keypress", function(e) {
console.log(e);
console.log(e.currentTarget.value);
});
Digging down through the first console output (e) shows that currentTarget.value = "a".
But the second console output (e.currentTarget.value) will show "".
If the user was then to type "b" into that same text box then:
Manually inspectng e and locating currentTarget.value displays "ab"; Dumping e.currentTarget.value from inside the event displays "a".
Can anyone explain how I can get this 'potential value' while inside the keypress event?
Not the prettiest of solutions (you can see the would be result just before it's reverted), but to save the trouble of discerning between arrow/control and input keys etc, you could store the original value in keypress and revert to that in keyup if needed (also storing the selection positions for complete reversion)
$(":input").keypress(function(e) {
$(this).data('orgValue', {value: this.value, pos: this.selectionStart, selend:this.selectionEnd});
}).keyup(function(e){
var val = this.value;
if(!somevalidation(val)){
var org =$(this).data('orgValue');
this.value = org.value;
this.selectionStart = org.pos;
this.selectionEnd = org.selend;
}
});
Example fiddle
Edit
Did some testing, but jquery makes predicting the outcome relatively easy. Not only does it fill the key property, it also fills other properties on its event on which the type of key can be checked. While testing charCode seems to be 0 for 'non input' keys.
The straight forward would be:
$(":input").keypress(function(e) {
if(!e.charCode)return; //is 0 for non input
var cur = this.value; //current value
var val = cur.substring(0,this.selectionStart)
+ e.key
+ cur.substring(this.selectionEnd);
return cur=== val //unchanged
|| somevalidation(val);
});
But that would not include deletes/backspaces, to handle those as well:
$(":input").keypress(function(e) {
var key,start = this.selectionStart ,end = this.selectionEnd;
if(e.charCode)
key = e.key;
else{
if(e.keyCode===8 || e.keyCode===46){
key = '';
if(end===start){
if(e.keyCode===8)
start--;
else
end++;
}
}
else
return true; //charCode is 0 for non input 46 = delete 8 = backspace
}
var cur = this.value; //current value
var val = cur.substring(0, start) + key + cur.substring(end);
return cur=== val //unchanged
|| somevalidation(val);
});
Fiddle
While testing this seemed to behave as expected. An other way might be have a hidden input field, send the keys there and examine its results, but the above should do the trick.

javascript input from keyboard without "prompt()"method popup (like pascal readln or gwbasic input or c++ scanf/gets/cin ...)

Hi, can javascript implement a function to read input from keyboard,
like pascal readln, gwbasic input, C scanf/gets, C++ cin?
Just to complete what follows:
&lt head>
&ltscript>function input(){
/* ?????????????? */
}
&lt/script>
&lt/head>
&ltbody>
&ltscript>
alert("Enter something");
a=input();
alert("You entered: "+a);
&lt/script>
I am an absolute beginner and I just tried to copy and paste some code from some books, unsuccesfully.
Tnx!
Yes, you can by calling a function once the keypress event is sent from the browser:
window.onkeypress = function(key) {
alert(key.charCode);
};
Will return the charCodes that can be translated with this list: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes.
This library might help you with the translation. It would then look like:
window.onkeypress = function(key) {
alert(GetChar(key.charCode));
};
Edit: Ok, you want to output the sequence when 'Enter' is pressed. To achieve that, you can use an array to store the pressed keys:
var chars = [];
window.onkeypress = function(key) {
var keyName = GetChar(key.charCode);
if (keyName !== 'enter') {
// store the current keyName
chars.push(keyName);
} else {
// output the pressed chars as string
alert(chars.join(''));
// clear chars array
chars = [];
}
};

Keys pressed at the same time

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.

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.

Categories