w3c / uievents

UI Events
https://w3c.github.io/uievents/
Other
147 stars 52 forks source link

Need property which gives the (base) unmodified key #247

Open tszynalski opened 4 years ago

tszynalski commented 4 years ago

I run a fairly popular website with text editors for many languages (www.typeit.org). I am currently using the legacy API (.charCode, .keyCode), and am looking into migrating to the new one. Unfortunately, it looks like the new API is missing some functionality of the old one.

Consider this example:

Suppose I wanted to use KeyboardEvent (the new API, not the legacy properties) to bind Alt+Z to some symbol. For example, if the user wants to type in Polish, I want to tell them to "press Alt+Z to type the Polish letter ż" (z with a dot). What does "Alt+Z" mean to the user? It means "hold Alt and press the key which has Z printed on it". (In this example, Alt includes AltGr, as they are considered the same by regular users.)

The KeyboardEvent API, as it is currently specced, does not allow me to do this reliably.

There is no property that would tell me that the user just pressed a key which is labeled "Z" on the keyboard.

Of course, the same problem will occur on any non-QWERTY keyboard layout with AltGr-activated characters. For now, I can still access the legacy property KeyboardEvent.keyCode – this returns the ASCII code of the key that would be inserted if the key were pressed without modifiers (in this case, Z). But the specification does not require browser developers to support this property. It is already deprecated according to MDN.

For this reason, I suggest one the following changes to the spec (in order of preference):

  1. Add a KeyboardEvent.baseKey property that would return the character that would be inserted if the current key were pressed without modifiers (with letters normalized to uppercase). There should be a special exception for AZERTY digits, i.e. pressing AltGr+2 should result in .baseKey == "2", even though a bare "2" keypress produces é (not 2) in AZERTY. (I believe this reflects the current browser status quo for .keyCode.)

  2. Make KeyboardEvent.keyCode mandatory.

This would give Web developers an official, standard way to refer to keys by their "keycap name".

garykac commented 4 years ago

Have you tried using getLayoutMap() API for this? It's still in the spec proposal stage, but you can try it in Chrome to verify it works for your use case (and let us know how it works for you).

WICG proposal: https://wicg.github.io/keyboard-map/ MDN: https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap Original Explainer: https://github.com/wicg/keyboard-map/blob/master/explainer.md

Making this information available is similar to exposing the user's current keyboard layout, and we need to be concerned about fingerprinting and privacy concerns. That's why we had many long discussions about the best way to make this information available.

Also note that the |keyCode| and |keyChar| were never properly specified, so there various differences in the implementations (especially with keys outside the standard A-Z part of the keyboard).

tszynalski commented 4 years ago

@garykac Gary, thank you for your response.

I have made a prototype reimplementation of my app using the KeyboardMap API. Generally speaking, it does the trick. If I match my bindings based on KeyboardMap.get(event.code), I can detect something like AltGr+Z, regardless of what the value of .key is for the current layout. Even though I had to make some workarounds, the API is certainly more predictable than relying on keyCode, which is nice.

However, I think adding a .baseKey property to KeyboardEvent would still be a good idea. The KeyboardMap solution forces me to load the whole keyboard map ahead of time, which magnifies privacy implications. For example, some browsers may decide that explicit user consent is required for such an operation. I really wouldn't want to degrade the experience of my users with a confusing popup. There's way too many of those already.

Other browsers may refuse to implement the API altogether. I see Apple has already decided the API is too invasive. I don't agree with that decision, but something like .baseKey would have a bigger chance of gaining acceptance, because it only leaks information when the user types something into the website (it cannot just be some banner ad). It does not leak much more information that what can already be gleaned from KeyboardEvent properties.

Another, minor issue is that, unlike .baseKey, KeyboardMap requires the page to monitor layout changes by means of the layoutchange event. This is extra work (albeit not very much), but more importantly, layoutchange isn't implemented by any browser yet.

On the other hand, I do appreciate that KeyboardMap has one advantage over .baseKey. For example, KeyboardMap allows me to detect an AZERTY layout before the user types anything. I can then adjust my UI (e.g. the onscreen keyboard, the displayed shortcut hints) accordingly to create a customized experience. I'm not sure if I would actually use this capability, but it's nice to know it's there.

I'd be very interested in hearing your thoughts.

PS. In the course of my testing, I have identified several problems with how KeyboardMap is specified. I will submit those issues to the KeyboardMap github shortly. I hope you'll find my suggestions helpful!

ggPeti commented 1 year ago

@garykac do you know of any movement on this?