Description
The Problem
So in this new world (since ~2020), we're supposed to use .key
(printable character) and .code
(scancode) instead of .keyCode
/.charCode
/.which
. This has a big problem:
- Suppose my webapp wants to listen for Ctrl-A and do Select All
- I don't want to use
.code === "KeyA"
because on an AZERTY (eg French) keyboard, that key is labeledQ
, and my shortcut is for Select All not Select Qll - I don't want to use
.key === "a"
because on eg a Russian keyboard, theA
key will have.key === "ф"
- Also if I wanted to listen for Ctrl-Alt-A, then
.key === "å"
for US English on Macs (in every browser), which is annoying
- Also if I wanted to listen for Ctrl-Alt-A, then
The closest to what I want is actually sad, old, deprecated .keyCode === 65
! Because .keyCode
was a kind of cross-platform version of OS virtual key codes (and was cross-browser compatible at least for alphanumeric keys).
Severity
It seems obvious to me that this is affects almost every webapp with keyboard shortcuts. Indeed, this seems like a much more common use case than games that want physical WASD controls. Even this obscure repo has 3 open tickets on it: #229, #247, #267
Non-Solutions
In #229, #247, and #267, @garykac offers Keyboard.getLayoutMap()
as a solution, which shipped to GA in Chrome in 2018. It has since been rejected both by Mozilla and by WebKit for privacy reasons, but to be clear it also doesn't solve the problem in the first place:
- for Ctrl-A, I believe that with a Russian keyboard layout,
keyboardLayoutMap.get("KeyA")
is specced to returnф
(altho this doesn't work for me in Chrome on Mac; instead it has an unreliable return value, see link for details)- by contrast,
.keyCode === 65
, same asA
on US English QWERTY
- by contrast,
- for Ctrl-2,
.code === "Digit2"
, but with French AZERTY,keyboardLayoutMap.get("Digit2") === "é"
, not2
- by contrast,
.keyCode === 50
, same as2
on US English QWERTY
- by contrast,
Proposed Solution
Rather than the hardware scancode that .code
is based on, .keyCode
is supposed to be based on the OS "virtual key code". In my testing, for alphanumeric keys, it's pretty cross-compatible, however for punctuation/symbols it's pretty inconsistent across browsers and reputedly platforms. Can we standardize a cross-platform "virtual key code" using the KeyA
etc names that respects, rather than ignores, the keyboard layout (QWERTY vs AZERTY vs Dvorak), but doesn't map to character input (ф
)?
To me, unifying 2 or 3 OS virtual key code tables sounds much simpler than mucking with keyboard layout priority lists.
Open Questions
Punctuation/symbols are inconsistent because it's pretty unclear what they should do. For example, instead of a /?
key, French AZERTY has a :/
key. Should that map to ANSI QWERTY /?
or ;:
? Or some other universal-ish "reference keyboard" (context, original)?
- The most interesting question is what do OS platform APIs do for these—and how consistent are they with each other? A sensible mapping to ANSI QWERTY is nice but cross-platform & cross-browser is good enough
- I'm also curious what Java does
Recommended Workaround
In the meantime, the situation for web devs appears to be:
- First, stick to ASCII for hotkeys, and don't use Alt or Shift with punctuation/symbols, only with alphanumeric, functional, arrow etc keys.
- Even if you don't care about international keyboard layouts now, hotkeys are painful to change later.
- For Alt-alphanumeric, beware that Windows treats Ctrl+Alt as AltGr.
- If, for now, you don't care about international layouts, it's easiest to just look at
.code
and ignore.key
, because of the Ctrl-Alt-A.key === "å"
problem- There are a bunch of non-Latin languages whose standard layouts are based on QWERTY that this should kinda just work for, actually
- If you start caring about international layouts, then I think you actually want to start ignoring
.code
, instead first looking at.keyCode
, which for alphanumeric keys should sensibly map back to standard ANSI QWERTY; and if.keyCode
indicates non-alphanumeric, then look at.key
.- I think for functional, arrow, numpad etc keys,
.key
should be the same as.code
; and otherwise for punctuation/symbols, in general there may not be a sensible mapping to an ANSI QWERTY/reference layout, but at least it'll tell you the actual punctuation typed, which hopefully is marked on the physical keycaps or understood by the end-user.
- I think for functional, arrow, numpad etc keys,