30

I am developing an application where I need to do some post-processing when the user presses CMD+LEFT on a particular text-box. I need to do this after the browser's default functionality (i.e. after it takes the caret to first position in current physical line).

The problem is keyup is not being triggered for the LEFT key (or any key for that matter) as long as the CMD key is down.

I tried this with CTRL and SHIFT keys and found that keyup gets triggered as expected for the secondary key. So if you do CTRL+LEFT and then release LEFT and then release CTRL, you get four events in total, 2 keydowns and 2 keyups. For the CMD key however, we get 2 keydowns, but only one keyup event (the one for CMD key itself when we release it in the end).

I tried this with SHIFT key and found that keyup gets triggered as expected for the secondary key. So if you do SHIFT+LEFT and then release LEFT and then release SHIFT, you get 4 events in total, 2 keydowns and 2 keyups. For the CMD key however, we get 2 keydowns, but only one keyup event (the one for CMD key itself when we release it in the end).

What could it be? Is there any way I can get keyup triggered for the LEFT key (or any key) when CMD is down?

I'm trying this with the latest Google Chrome on OSX 10.9.5. The behaviour is exactly the same on Firefox too. So this isn't a Chrome issue.

Demo: http://jsfiddle.net/techfoobar/xu0o11nh/4/

Essentially:

$('#mytextbox')

    // this gets correctly triggered for the meta key as well as the secondary key
    // when you press CMD and LEFT in sequence, you get two lines in the console one for 
    // the CMD key and one for the LEFT key
    .keydown(function(_e) {
        console.log('Keydown: ' + _e.keyCode);
    })

    // however, if I release the LEFT key (while keeping the CMD key down)
    // this does NOT get triggered for the LEFT key
    .keyup(function(_e) {
        console.log('Keyup: ' + _e.keyCode);
    });
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
techfoobar
  • 65,616
  • 14
  • 114
  • 135
  • In chrome I get all events. jQuery send a key in the event which is called `ctrlKey` where you can check if ctrl is pressed... i dodnt check which is cmd but you can easily find it out by console complete event – Mephiztopheles Dec 15 '14 at 09:57
  • @Mephiztopheles - The problem is only with the CMD key (`event.metaKey` courtesy jQuery). – techfoobar Dec 15 '14 at 12:52
  • Either I didn't understand your problem or I couldn't simulate it. Here is my console: `70(index):34 Keydown: 17 (index):34 Keydown: 37 (index):37 Keyup: 37 (index):37 Keyup: 17` – Marco Aurélio Deleu Dec 16 '14 at 19:47
  • if you check my console, you'll see that I held CTRL and it triggered Keydown for 70 times, then I triggered down LEFT KEY, then I triggered up LEFT KEY, then I triggered up CTRL. http://puu.sh/dxnVq/8a9a74a2f8.png – Marco Aurélio Deleu Dec 16 '14 at 19:50

3 Answers3

6

This is known behavior with the meta key, and there is unfortunately no known workaround.

For your case, you may consider implementing the default browser behavior yourself (how to do that), then implement the custom behavior you need, and finish it off with _e.preventDefault();

caseyWebb
  • 1,997
  • 17
  • 18
  • Thank you. So thats that. Known issue. No easy workaround as well. Custom implementing CMD+LEFT (taking caret to first position in physical line of text considering wrapping, etc.) is too complicated for my simple purpose. – techfoobar Dec 17 '14 at 04:37
  • 1
    Your first link is broken. Here's a working link using the wayback machine. 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/ – Aadit M Shah Nov 20 '19 at 12:27
2

This is almost certainly to do with system set 'hot keys'.Apparently according to the docs this missing keyup event is expected behaviour too.

When i do cmdspace i dont even get a keydown event for the space as the spotlight window appears.

On your mention of the ctrl key: because i have 'spaces' set up when i ctrlleft or ctrlright i get no left or right keydown events fired, however ctrlup or ctrldown at least fire their keydown events.

I think it would be difficult to assume the use of a system that does not have the "default" hot keys setup.

Community
  • 1
  • 1
haxxxton
  • 6,422
  • 3
  • 27
  • 57
  • Hi, Thank you very much for the answer. But on a Mac, CMD+Left/Right does not have any *system* action, only a *component* (textbox) action - i.e. taking the caret to the first position in the current horizontal line of text. This is comparable to Shift+LEFT, which extends selection to the left by one position - but with Shift, we get 4 key events as outlined in the question, i.e. I can do post processing after Shift+Left is done extending the selection. The case with CMD+Space is understandable because it has a *system* action - Spotlight, same with Ctrl+Left/Right (mission control). – techfoobar Dec 12 '14 at 03:29
  • Also, the behaviour is exactly the same in Firefox, so this isn't a Chrome issue. – techfoobar Dec 12 '14 at 03:32
  • Agreed, I would totally not expect this to be Chrome specific behaviour. Perhaps a solution might lie in detecting the change in caret position: http://stackoverflow.com/a/19803814/648350 . However, this still is bound to the `keydown` event so not sure if it's what you're looking for – haxxxton Dec 12 '14 at 03:57
  • The only (ugly) solution I can think of as of now is capturing the last keydown and doing the post processing inside a setTimeout set for after a few milliseconds. Since the browser keeps firing keydowns for the secondary key as long as it is kept pressed, this should be possible. But imo not a very clean way to do it! – techfoobar Dec 12 '14 at 04:00
  • I suppose you can assume that given the `cmd`+`left` functionality happens immediately, any hold of the `left` key doesnt act to serve any better purpose (eg. unlike `shift`+`left` would) – haxxxton Dec 12 '14 at 04:03
1

don't think you'll need a settimeout.

i've changed the code to detect 2 keydowns. this can be refactored further to a cleaner code.

http://jsfiddle.net/7nd7hf16/1/

var cmdDown = false;
$('#foo')
.keydown(function(_e) {
    if(_e.keyCode == 91)
        cmdDown = true;

    if(cmdDown && _e.keyCode == 37)
        console.log('cmd + left'); 

    console.log('Keydown: ' + _e.keyCode);
})
.keyup(function(_e) {
    if(_e.keyCode == 91)
        cmdDown = false;

    console.log('Keyup: ' + _e.keyCode);
})
.focus();
hackerone
  • 315
  • 1
  • 4
  • Hi, Thank You for the your answer. The problem is not in detecting the keyup for the cmd key (which your answer addresses), but the keyup for the secondary key as long as the cmd key is kept pressed. Also, note that jQuery gives us `_e.metaKey` for detecting whether the cmd is pressed (i.e. we do not need to detect it ourselves as in your answer). – techfoobar Dec 16 '14 at 04:59
  • `if(cmdDown && _e.keyCode == 37) console.log('cmd + left');` i think you've skipped the above code.. so, on cmd keydown using keycode or metakey you set a `cmdDown` to true and on key up you change it back to false.. since you know the cmd key state you can now check for the second key pressed.. – hackerone Dec 16 '14 at 11:13
  • Thats right. I can get the **keydown** event correctly for `CMD+` like you've shown - The only problem is in getting the **keyup** event for the second key (LEFT in our case) as long as CMD is kept pressed down. – techfoobar Dec 16 '14 at 12:22