Detecting ctrl+z (and other control combos) in paper.js - javascript

I'm trying to enable editing commands in my paper.js application (such as CTRL+z for 'undo').
Detecting individual letter keys works great, and I can detect modifier keys that are held during mouse events, but I'm having trouble writing an event handler that detects combinations of CTRL and letter keys.
Based on the examples given by fabric.js, what I would expect is a key handler that looks something like this:
function onKeyDown(event) {
if (event.key == 'z' && event.modifiers.control){
//do a thing!
}
}
However, this doesn't work! Weirdly enough, the conditional block never fires. To investigate this, I wrote the following diagnostic handler...
function onKeyDown(event) {
console.log(event.key);
console.log(event.modifiers.control);
}
... and tried it out with various keyboard inputs with some interesting results:
CTRL key only
Key: control
Control: true
z key only
Key: z
Control: false
z key pressed while holding CTRL
Key:
Control: true
These results suggest that the string returned by event.key is different depending on whether the control modifier is held down when another key is typed. Something weird is happening here!
Based on this, how can I detect both of these keys being pressed at the same time?

Here are a couple of vanilla Javascript solutions that should help you:
Solution 1
Check which keycode was pressed down and if the shiftkey is down using native the event object.
function handleKeyDown(evt) {
if (evt.which === 90 && evt.shiftKey) {
// do a thing!
}
};
Solution 2
Keep a global variable for the detecting if the shift key is down and use that in your keydown handler. You'll also need to reset it with a keyup event handler.
var shiftKeyDown = false;
function handleKeyDown(evt) {
if (evt.which === 17) {
shiftKeyDown = true;
} else if (evt.which === 90 && shiftKeyDown) {
// do a thing!
}
};
function handleKeyUp(evt) {
if (evt.which === 17) {
shiftKeyDown = false;
}
};

TL;DR: You can use event.key.charCodeAt(0) to detect the strange character codes returned by CTRL+Z and other CTRL+key combinations.
As it turns out, the CTRL+z combination is special.
The key being returned in this case...
z key while holding control
Key:
Control: true
... looks like an empty string, because the keycode being passed to the event handler corresponds to the special CTRL+z combination, which results in an unprintable character.
To detect this special character, I modfiied the diagnostic handler...
function onKeyDown(event){
console.log("Key: " + event.key);
console.log("Control: " + event.modifiers.control);
console.log("KeyCode: " + event.key.charCodeAt(0));
}
... and tested the same keyboard combinations as before:
CTRL key only
Key: control
Control: true
KeyCode: 99
z key only
Key: z
Control: false
KeyCode: 122
z key pressed while holding CTRL
Key:
Control: true
KeyCode: 26
This means that the special CTRL key combinations can be detected using an event handler like this:
function onKeyDown(event) {
if (event.key.charCodeAt(0) == 26){ // detect the special CTRL-Z code
// do a thing!
}
}
It should be noted that this approach will not work for detecting the control key on its own, since 99 is NOT the character for CTRL, but rather for "c", the first character in the string "control" returned by event.key. For that, you'll still want to use event.modifiers.control.

function onKeyDown(event) {
if (event.event.ctrlKey && event.key == "z") {
//do something
}
}
This should work.

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>

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>

addEventListener('keydown') JavaScript bug?

I'm trying to create a game in HTML5 and I've come to the point where I need to enable keyboard input.
So Here's my code:
window.addEventListener('keydown', function(e) {
let key = e.keyCode;
console.log(key);
if (key == 37) {
canvas.player.x -= 5;
}
if (key == 38) {
canvas.player.y -= 5;
}
if (key == 39) {
canvas.player.x += 5;
}
if (key == 40) {
canvas.player.y += 5;
}
}, false);
Where canvas is the canvas object and canvas.player the player object. It works, but not very well... Let's say I'm pressing (and holding down) the right arrow key (39) and than press the down arrow key (40) the player is not moving to the right anymore since we last pressed the down arrow key. Works fine. Until I only release the down arrow key while still pressing down the right arrow key. So I never released the right arrow key. Than the player stops and the browser doesn't seem to understand that I'm pressing the right arrow key.
You can easily see this in the console log of this fiddle.
Does anybody has a solution for this problem? A way to detect the keycode anyways?
Keyboard IO.
Generally IO events like the mouse, touch, and keyboards should only be used to get the current state of the devices they are listening to. The job of reacting to input is done in your game.
one way to handle keyboard input is as follows
// this defines what keys you are listening to and
// holds the current state of the key true for down false for up
const keys = {
ArrowUp : false, // list the keyboard keys you want to listen to
ArrowDown : false,
ArrowLeft : false,
ArrowRight : false,
};
// the event listener listens for key events
function keyEvents(e){
if(keys[e.code] !== undefined){ // check if its a key we are listening for
keys[e.code] = event.type === "keydown" ; // set the state up or down
e.preventDefault(); // stop default action
}
}
addEventListener("keyup",keyEvents); // set up the listeners
addEventListener("keydown",keyEvents);
Then in your game's main loop or called from there check the key state and perform the action that the state requires..
if (keys.ArrowDown) { player.y += 5 }
if (keys.ArrowUp) { player.y -= 5 }
if (keys.ArrowLeft) { player.x -= 5 }
if (keys.ArrowRight) { player.x += 5 }
It may be a bug, I thought that it used keyboard events but on mac it seems to repeat the character when held but when typing normally on a mac holding down a key doesn't do that.
I have done a workaround before using a combination of keydown and keyup to set booleans for each of the keys. Then an interval using something like setInterval which checks each of the booleans for each of the keys. If they are true then I carry out the action for that key.

Lookup table for multiple Key Press

I am trying to create a look up table ( as i have large number of keys to handle) for handling my keys functionality. So far i have been able to handle single key presses as shown below:
(function(){
document.body.addEventListener('keydown',keysHandler,false);
var keyLookUp = {37:leftKey,39:rightKey,40:DownKey...etc}
function keysHandler(e){
event = e || window.event;
var keycode = event.which || event.keyCode;
keyLookUp[keycode](e);
}
function leftKey(e){
}
function rightKey(e){
}
function DownKey(e){
}
})();
How can i modify the above code to handle the multiple key press functionality also?? like pressing Shift + left key
How can i modify the above code to handle the multiple key press [such as] pressing Shift + ←
The keys Shift, Ctrl, Alt and Alt Gr are modifier keys, this means that they're sent as part of the event you call e and are stored as e.shiftKey, e.ctrlKey, e.altKey and e.altGraphKey, respectively.
So what does this mean for you? If you want to keep your current structure, I see you as having three choices; you can
write completely different functions for the modified versions, e.g.
keyLookUp['37sc'] = leftKeyShiftCtrl;
keyLookUp[
keycode
+ (e.shiftKey ? 's' : '')
+ (e.ctrlKey? 'c' : '')
+ (e.altKey ? 'a' : '')
](e);
look at these values in you current handlers
// inside fn
if (e.shiftKey) {/* different code path */}
pass these as parameters into your handlers (which take extra args)
function leftKey(e, shift, ctrl, alt) { /* ... */ }
keyLookUp[keycode](e, e.shiftKey, e.ctrlKey, e.altKey);
For any combination of keys, you need to track keydown and keyup, see this question

How can I know a 2 last pressed key on a keyboard

Is it possible to know, what 2 last keys was pressed on keyboard?
How can I do it?
I need to compare this keys, and if they are the same - make some function.
As example, if someone press Enter - this is a first function, if after Enter he press a SPACE- this is a second function. If after the ENTER he press a Ctrl - this is a third function.
So, I hink that only onw way to do it - is make a 2 var with current and previous key value, and make a IF ELSE IF function
:-)
This is a way, how I get a current key value
$('#text').keydown(function(event) {
$('#show').text(event.keyCode);
});
Or, the BETTER question! (I saw it right now)
Directly in this editor, after I press doublespace - it jump to anothe line in live-preview.
How it works? I'm not sure, but I thinks that I need is almost the same.
Thank you very much!
I think storing the last key pressed and checking both the old and new keyCode in the event handler is a good way to do this.
$('#text').keydown((function() {
var lastKey = undefined;
return function(event) {
// here you have both the old and new keyCode
lastKey = event.keyCode;
};
})());
Also, the two spaces thing is Markdown interpretation.
You could start with a very simple jQuery plugin:
(function($) {
$.fn.keyWatcher = function() {
return this.keydown(function(evt){
var $this = $(this);
var prevEvt = $this.data('prevEvt');
$this.trigger('multikeydown',[prevEvt,evt]);
$this.data('prevEvt',evt);
});
};
})(jQuery);
This will raise a new event (multikeydown) whenever a keydown event is usually raised. This will either provide just the keydown event from the current key press, or if there is a previous one it will provide that too.
Usage could be something like:
$(document).keyWatcher().bind('multikeydown',function(evt,prevKey,currKey){
alert('prevKey = ' + ((prevKey) ? String.fromCharCode(prevKey.keyCode) : '(none)'));
alert('currKey = ' + String.fromCharCode(currKey.keyCode));
});
Live example (press keys on the lower right hand pane in jsfiddle): http://jsfiddle.net/9VMUy/1/
The event provided in prevKey and currKey parameters to the event handler contain the original keydown events, so you have full access to the keyCode, the ctrlKey and shiftKey properties etc.
This is probably not the best solution but see if you can use it or parts of it
jsFiddle
var clicks = 0
$("#text").one("keypress", function () {
clicks = 1;
var KeyCode1 = event.keyCode;
$('#result').text(KeyCode1);
$("#text").one("keypress", function () {
clicks = 2;
var KeyCode2 = event.keyCode;
$('#result').text(KeyCode1 + "\n" + KeyCode2);
});
});

Categories