Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative for event's deprecated KeyboardEvent.which property

MDN states that KeyboardEvent.which is deprecated. How can I substitute it for a non-deprecated version?

For example, I have the following:

window.onkeydown = (event) => { console.log(event.which); } 

I thought event.key.charCodeAt() could substitute event.which, but this won't work for keys such as ALT, CTRL or ENTER, and it only works if event.key.length === 1:

window.onkeydown = (event) => { console.log(event.key.charCodeAt()); } 

To recap, event.which != event.code and event.which != event.key, therefore I am unable to simply use event.key.

Is there a substitute for event.which which detects combination keypresses including ALT, CTRL or ENTER?

like image 637
flen Avatar asked Mar 14 '18 13:03

flen


People also ask

What can I use instead of deprecated keyCode?

You should avoid using this if possible; it's been deprecated for some time. Instead, you should use KeyboardEvent. code , if it's implemented. Unfortunately, some browsers still don't have it, so you'll have to be careful to make sure you use one which is supported on all target browsers.

Why is charCode deprecated?

Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes.

What is KeyboardEvent?

KeyboardEvent objects describe a user interaction with the keyboard; each event describes a single interaction between the user and a key (or combination of a key with modifier keys) on the keyboard. The event type ( keydown , keypress , or keyup ) identifies what kind of keyboard activity occurred.


1 Answers

TL;DR: These are the rules you should follow:

  • When getting text input from the user, use the keypress event along with e.key
  • For shortcuts and other combinations, the built-in way is to use keydown/keyup and check the various modifier keys. If you need to detect chords, you may need to build a state machine.

Background

Keyboard input is split into two phases - keydown/keyup pairs, which track physical keys being pressed, and composed characters that combines multiple sequences of keys to compute a character.

Getting "text"

If you want to know what the operating system thinks the composed sequence is, you should use KeyboardEvent.key

Sample code:

document.getElementById('a').addEventListener('keypress', e => console.log(e.key));
<input id="a" type="text" placeholder="type here">

The reason you want to do this most of the time is because many languages compose characters with several keypresses. The easiest for US-101 keyboards to understand is pressing the shift key + a is A, compared to just pressing a. For languages like Russian, with the altgr dead key, this becomes especially important.

The point I am trying to make is that doing all of this work yourself - detecting key sequences and determining the correct text output is a hard problem™. It is the job of the operating system for a reason.

Now, for older browsers, you may not want to use e.key for lack of older support. Then you can fall back to things like which, or other non-standard approaches.

At some point in the future, keypress may be removed by browsers. The beforeinput event is supposed to be the replacement. However, that event is only supported in chrome, so I'm omitting in here for brevity's sake.

Getting keystrokes

Now then, suppose you are not tracking text, but rather key sequences. This is for things like games, or listening to ctrl-c etc. In this case, the correct thing to do is to listen to keydown/keyup events. For modifier keys, you can simply listen to the ctrlKey, shiftKey, and metaKey properties of the event. See below:

document.getElementById('a').addEventListener('keydown', (e) => {    const states = {      alt: e.altKey,      ctrl: e.ctrlKey,      meta: e.metaKey,      shift: e.shiftKey,    };    const key = e.key;    const code = e.code;    console.log(`keydown key: ${key}, code: ${code}`, states);  });
<input id="a" type="text" placeholder="press ctrl">

As an example, when pressing shift-o on my keyboard, I get the following:

keydown key: Shift, code: ShiftLeft {   "alt": false,   "ctrl": false,   "meta": false,   "shift": true } keydown key: O, code: KeyS {   "alt": false,   "ctrl": false,   "meta": false,   "shift": true } 

Hopefully the states part is pretty self-evident. They say whether that modifier key was pressed while the other key is down.

The difference between key and code has to do with keyboard layouts. I am using the software dvorak layout. Thus when I type the s key, the scan code that goes to the operating system says s, but then the OS converts that to o because it's dvorak. Code in this case always refers to the scan code (physical key being pressed), while the key corresponds to the operating system's best-effort to figure out what the "text" will be. This isn't always possible, especially with other languages. Again, this is why using the key for the keypress is the right way to go about it.

3rd party libraries

If this doesn't sound particularly easy, that's because it's not. The last time I was looking at this, I came across the mousetrap library, although I'm not sure I would recommend it, given some of the issues I found. It does, however, show an example of building a state machine to track key chords.

Addendum

This is also why you need to track keydown/keyup if you want to eat keystrokes. Since there is no "text" for ctrl+c, you won't get a proper keypress, and thus the browser will natively handle it. If you want to run your own behavior, you need to e.preventDefault() on the keydown itself. (Some of the followup events like copy can also be cancelled, but that's not universally true)

If you also just need to track keys inserted after-the-fact into an input field (or contenteditable div), see the input event.

History: Updated 8/2019 to change keypress->beforeinput

like image 65
AnilRedshift Avatar answered Sep 30 '22 11:09

AnilRedshift