This is Mac only problem; I've tried this on windows and it works fine.
I have a script that saves which keys are pressed on keydown and deletes them on keyup.
$(function(){
var keys = [];
$(document).keydown(function(event) {
keys[event.which] = true;
});
$(document).keyup(function(event) {
delete keys[event.which];
console.log(keys);
});
});
All I am doing right now is console logging whatever is left after the keyup, which should be nothing. This works as expected when you press any number of keys simultaneously.
HOWEVER, when you press command and any other key, the other key is NOT released! So console.log will show that key to be true. This will remain true until you press that key itself.
This only happens on a Mac, and it only happens when one of the keys pressed is the Command key. Here is a very simple plunker with the above example.
Any ideas?
The Mac is modifying your key whenever you press Command, and thus the normal keyup event never fires.
C = command key and K = non-command key
As you press C and K, they register normally. While they are both simultaneously pressed, the Mac captures K and modifies it. In modifying K, the Mac somehow makes K's keyup event not fire as it is supposed to. C's keyup works as expected, however.
Since K's keyup never actually fires, it won't correctly delete the matching element from keys. Later on when you press K without C, the keydown event overwrites the existing keydown in keys. And when K's keyup correctly fires, it works as expected.
In addition to all the normal keys used to input ASCII characters,
keyboards typically have many special purpose keys that do other
things. These do not necessarily generate the same events as normal
keys, and they show less consistency across browsers.
JavaScript Madness: Keyboard Events. Potentially helpful article for all key-related problems.
I can't fix this Mac issue, but here is my way of getting around it.
This answer will help you if you are trying to have keyboard-shortcut behavior, where the user presses CMD+S to do save, or something like that. This answer does not apply to people who may be building a game or something where their keyboard's keydown states need to be known at each run frame. Sorry!
In the KeyboardEvent returned by keydown, you can do the following
$(document).keydown(function(keyboardEvent) {
if (keyboardEvent.metaKey){
// on Mac, CMD is down ...or Meta key is down on pc
console.log(keyboardEvent.meta + " & command key are down")
switch (keyboardEvent.which) {
...
}
}
});
If your keyboard shortcut overlaps with the browser's, you need to make sure to cancel the propagation of the keyboard event,
keyboardEvent.preventDefault()
I hope this helps people who want keyboard shortcut functionality that is Mac compatible!
Related
I'm building a web application with a fullscreen canvas that needs to react to continuous and discrete keyboard input.
My current approach is similar to the one in this question: JavaScript keep track of which keys are down accurately.
I maintain a set of which keys are currently pressed using keyEvent.code to differentiate keys. I query this set in the requestAnimationFrame loop of my application. I update the set in event handlers for the 'keyup' and 'keydown' events attached to window.
The Problem
When pressing and releasing Cmd+Z to undo, the 'keydown' event fires but the 'keyup' event does not (at least in the latest Firefox, Chrome, and Edge on macOS 10.15.6).
Consequently, my set contains an entry for 'KeyZ' even though the Z key on the keyboard is not being held. Similarly, Cmd+C, Cmd+V, and many other system shortcuts seem to hijack the 'keyup' event.
Why am I not getting a 'keyup' event in this case? What can I do to ensure that my set accurately reflects the state of the currently held keys on the keyboard?
What I've Tried
This seems like it could be related to event propagation, so I tried using keyEvent.stopPropagation() and keyEvent.preventDefault() in the keydown event handler.
I've tried pressing Control+Z instead, which does fire a 'keyup' event.
Problem Reproduction
const listenerTarget = window;
const useCapture = false;
const heldKeys = new Set();
listenerTarget.addEventListener('keydown', (keyEvent) => {
const code = keyEvent.code;
if (!keyEvent.repeat) {
console.log(`${code} went down`);
heldKeys.add(code);
} else {
console.log(`${code} went down (repeat)`);
}
keyEvent.stopPropagation();
keyEvent.preventDefault();
}, useCapture);
listenerTarget.addEventListener('keyup', (keyEvent) => {
const code = keyEvent.code;
// Why does this not fire if
// the 'keydown' happened in combination with
// a Meta key?
console.log(`${code} went up`);
heldKeys.delete(code);
}, useCapture);
Workaround Update (11/03/2020)
I found When CMD key is kept pressed, keyup is not triggered for any other key and http://web.archive.org/web/20160304022453/http://bitspushedaround.com/on-a-few-things-you-may-not-know-about-the-hellish-command-key-and-javascript-events/, which indicate that this is known behavior. My current workaround is to not add codes to the set of held keys if keyEvent.metaKey is set. Doing this at least ensures that the set of held keys doesn't contain phantom entries. I'll keep this question open in case someone can think of a better approach.
For me it works just fine on the lates chrome version..
Sorry.
This isn't super helpful, but there's a bunch of good info here: https://unixpapa.com/js/key.html. You've probably already seen that in other answers though.
I say not super helpful because it doesn't really answer your particular question, but it does show that there's definite weirdness between browser versions and both modifier keys and branded keys. I suspect the answer is that the branded keys like the windows key and cmd key just act weird because they tend to trigger system level events. I would just use different keys, but hopefully someone else will know a way around this other than "use different keys".
For windows people, you can produce really similar results by doing Windows Key + Z. You'll only get up event for Z and no down events.
I'm just wondering what the preferred way to preform a ctrl + click action in leadfoot is. In java I would have used the Actions class and used keyDown, but since we have moved to a JS based framework I'm a complete fish out of water!
I've seen in the api that there is a pressKeys function but it doesn't seem to do what we need. I've thought about using jQuery to do this but I would really rather keep it in the current framework.
Any help greatly appreciated.
Peter
You can use pressKeys, for example:
command.moveMouseTo(myBtn)
.pressKeys(keys.CONTROL)
.clickMouseButton()
.pressKeys(keys.CONTROL)
A good thing to remember about pressKeys (https://theintern.github.io/leadfoot/Command.html#pressKeys)
keys: The text to type in the remote environment. It is possible to type keys that do not have normal character representations (modifier keys, function keys, etc.) as well as keys that have two different representations on a typical US-ASCII keyboard (numpad keys); use the values from leadfoot/keys to type these special characters. Any modifier keys that are activated by this call will persist until they are deactivated. To deactivate a modifier key, type the same modifier key a second time, or send \uE000 ('NULL') to deactivate all currently active modifier keys.
TheIntern/LeadFoot provides you a function execute. You can trigger any event from this function using JS.
.execute(function() {
//You can even access window from here
$("#someId").click() //example
//or try something like this
e = jQuery.Event("keydown");
e.which = 50;
e.ctrlKey = true;
$("input").trigger(e);
})
To trigger keyevent follow these links:
jquery trigger ctrl + click
How to trigger key combo with jQuery
Related: JavaScript KeyCode vs CharCode
Here is some code you can try at home or in a jsfiddle:
el.addEventListener( 'keyup', function( e ) {
console.log( 'Keyup event' );
console.log( e.keyCode );
} );
el.addEventListener( 'keypress', function( e ) {
console.log( 'Keypress event' );
console.log( e.keyCode );
} );
Why is the keyCode different?
I can understand why one should use keypress only, but what I don't understand is how two key events, given the same hit key on the keyboard, give different keyCodes.
PS: I'm not worrying about legacy browsers support, I tried this in Chrome and was surprised, and couldn't find an explanation.
The events are for completely different purposes. Use keyup and keydown for identifying physical keys and keypress for identifying typed characters. The two are fundamentally different tasks with different events; don't try to mix the two. In particular, keyCode on keypress events is usually redundant and shouldn't be used (except in older IE, but see the linked document below for more on that); for printable keypresses it's usually the same as which and charCode, although there is some variation between browsers.
Jan Wolter's article on key events, already linked to in another answer, is the definitive word on this subject for me and has tables describing what each of the different properties returns for each type of key event and each browser.
There is a good article on quirksmode.org answering exactly that question. You might also want to look at Unixpapa's results.
Well, I stumbled upon one difference when i was trying to copy user's entry from one input of the form to some other part of the form , which I had locked for my for users to edit.
What i found was, that whenever a user moved to the next label using key upon completing the input, one last keyboard entry was missed in the copied entry when I used eventListener to keypress and this got resolved on using keyup.
So, in conclusion Keypress listens to the state at the instant when the key was pressed, leaving aside the result of keypress, whereas keyup listens to the system status after the key has been pressed and includes the result of the keypress.
I'm not sure why this is happening. Similar things have happened, but they're typically due to keyloggers or something along those lines.
My javascript is simply bugging out, or something.
When I use this link and press W, I get 87 as a response.
However, when I run my own code, which looks simply like this:
var keydown = function(e) {
console.log(e.keyCode); //I've also tried charCode
}
window.addEventListener("keypress", keydown, false);
I get the number 119 when I press W.
This is occurring both in Chrome and in IE.
What gives?
Actually, keyup and keydown for identifying physical keys and keypress(redundant) is for identifying typed character so using keypress event, W/capital(87) and w/lower(119) gives different results but keydown will always give you 87. So, as other answer stated, you should use keydown.
Read more on quirksmode.org.
Therefore, onkeydown/up keyCode always holds the key code. onkeypress
you can find the actual character the user typed by evt.charCode ||
evt.keyCode.
Note: as the keypress event isn't covered by any official specification, the actual behavior encountered when using it may differ across browsers, browser versions, and platforms.
Source: http://api.jquery.com/keypress/
Maybe you should use keydown?
What I'm trying to do is put key codes in an array, to do some interesting stuff with later on. So, I capture the keystrokes, get the caret position and put the key code in the array (with some help from MooTools):
var keyArray = [];
$('form').addEvent('keyup', function(event) {
var pos = document.activeElement.getCaretPosition();
keyArray[pos] = event.code;
});
Generally speaking, this works great. However, I noticed some undefined values in my array when showing the complete array in my console. Exploring this further, I found out that when typing quickly, the caret position seems to lose track, or being quick/slow to respond. I've made a jsfiddle to demonstrate this: http://jsfiddle.net/HQVR8/1/
If you type quickly in this example, you'll see a caret position sequence like
- 1 - 2 - 3 - 5 - 6 - 6.
But when typing slowly, it's
- 1 - 2 - 3 - 4 - 5 - 6.
Of course, the trouble now when typing quickly is that I have a undefined value in my array and I overwrite one array item. So the result is different.
My question is if I can somehow let the caret position keep track. I've tried using the 'native' selectionStart instead, but results are the same. I also tried capturing the caret position in a keydown event and putting it in an array in the keyup event. No difference. I'm wondering if using little pauses (ie. forcing the user to type slower) might solve it, but this feels like a hack and I would prefer a 'proper' solution. Hopefully, there is one.
you are basically making a mess by using an array instead of an object.
array indexes are dodgy and can create sparse arrays if you are not careful. for instance:
var foo = [];
foo[0] = "one!"; // foo.length = 1;
foo[2] = "two!"; // foo.length = 3, foo[1] = undefined
so if you type too fast and skip the return value, it is probably doing just that. also, pasting etc can push caret further down...
you can probably use an object though there are no guarantees on the order of keys in vs keys out in all browsers - particularly webkit, who tend to reorder and put numeric keys at top of object key loops... but if you prefix the key like "pos" + caretIndex, you ought to get FIFO
one way to solve your need to extract the actual codes w/o the undefined is via Array.filter.
eg.
retArray = Array.filter(retArray, function(el) {
return el !== undefined;
});
With an object, you can just do:
console.log(Object.values(foo));
After some more tests, it appears to be a behavior specific to keyup. When I use keydown I do get a consistent sequence: http://jsfiddle.net/HQVR8/3/
One disadvantage is that keydown is a step behind keyup when you're doing the 'value collecting' I'm doing, but in my setting, this is only a minor issue.
The difference in behavior appears odd to me: when you press four keys at a time, the keyup displays the same caret position for all of them, while keydown is showing four separate caret positions. This seems odd because you press them at once, but also depress them at once, so the caret 'readings' should be the same.
go to the JSfiddle and fiddle with this:
a) depress 'q', then depress 'w', then release 'q', then release 'w' (fast enough to avoid autorepeat). That kind of sequence happens quite often when you type 'quickly' with more than one finger :).
b) leave any key depressed long enough for the autorepeat to kick in.
The differences are in plain view
Basically, keyUp fires when the physical key is released, but
another key can be depressed before the previous one is released
keyDown is called in case of autorepeat, while keyUp isn't
Since carret position and whatever text area updates are kept in sync with keyDown, you will simply miss the intermediate steps when monitoring keyUp.
In case (a), the keyUp handler sees carret pos jump from 0 to 2 as 'q' is released because two characters have been typed before the first key was released. The release of 'w' does not change carret position, so the handler ends up storing both q and w at position 2.
In case (b), the handler misses the repeats completely, and stores only the last instance of the key.
As a conclusion, using KeyUp will never achieve the intended meaning.
keyDown could, but personnally I would rather hook the changed event.
I reckon it is no less reliable to detect a bot filling the field (after all, a legit user could want to paste the password too), and you won't have to bother with control keys like backspace or whatever other non-keyboard means of clearing the input.
You could use keyUp as an extra paranoid check, as long as you don't expect to reconstruct the exact input. For instance, you could simply check that the released key matches the character at current cursor position. But frankly I'm not sure it is worth the bother.