1

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.

Julian Ceipek
  • 525
  • 3
  • 13

2 Answers2

0

works

For me it works just fine on the lates chrome version.. Sorry.

LK.
  • 1,781
  • 1
  • 10
  • 5
0

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.

Dest987
  • 31
  • 3