Problem trying to solve.
Map every keyboard (input source / layout) back to EN-US.
Why would I want to do this, you ask?
At work we have a QRcode scanner that can be connected to any machine (via USB), and it will simulate some keyboard input when you scan a QRcode.
Scanner works with US Standard 101 layout (EN-US), and it knows for example that character Y
has scancode 0x1c
and Z
will have scancode 0x1d
, and it will simulate a scancode for a specific key on keyboard (USB keyboard scancodes).
For example we have a QRcode that represent this URl.
https://amayzyng.com/iamandrewluca
This will type QRcode scanner for 5 keyboard (input source / layout)
// English (ABC) https://amayzyng.com/iamandrewluca // German (ABC - QWERTZ) httpsÖ--amazyzng.com-iamandrewluca // Dvorak dyyloSzzamaf;fbivjrmzcamabep.,ngja // Russian реезыЖ//фьфнянипюсщь/шфьфтвкуцдгсф // Romanian - Standard httpsȘ//amayzyng.com/iamandrewluca
As you see the results for each one are the same 🙃
This is why we need to map US Standard 101 layout scancodes to EN-US
Requirements
Solution is based on KeyboardEvent.code that represents physical code of the key. Also KeyboardEvent.code
(status: Working Draft) is not supported in all browsers.
Here is a polyfill if you want to support more browsers
Solution for the problem. Let's get started!
This is the only piece of HTML
you'll see in this post 🙂
The rest will be mighty JavaScript
<input type="text" />
First of all let see what are most events used on an input, and in what order they are trigerred.
const input = document.querySelector('input') input.addEventListener('focus', info) input.addEventListener('keydown', info) input.addEventListener('keypress', info) input.addEventListener('input', info) input.addEventListener('keyup', info) input.addEventListener('change', info) input.addEventListener('blur', info) function info(event) { console.log(event.type, event.target.value) }
The only way to catch typed character into input is to watch for keypress
event.
At this phase character does not appear in input.value
function onFocus (event) { info(event) } function keyDown (event) { info(event) } function keyPress (event) { info(event) // this 2 calls will stop `input` and `change` events event.preventDefault(); event.stopPropagation(); // get current props const target = event.target const start = target.selectionStart; const end = target.selectionEnd; const val = target.value; // get some char based on event const char = getChar(event); // create new value const value = val.slice(0, start) + char + val.slice(end); // first attemp to set value // (doesn't work in react because value setter is overrided) // target.value = value // second attemp to set value, get native setter const nativeInputValueSetter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, "value" ).set; nativeInputValueSetter.call(target, value); // change cursor position target.selectionStart = target.selectionEnd = start + 1; // dispatch `input` again const newEvent = new InputEvent('input', { bubbles: true, inputType: 'insertText', data: char }) event.target.dispatchEvent(newEvent); } function keyUp (event) { info(event) } function onInput (event) { info(event) } function onChange (event) { info(event) } function onBlur (event) { // dispatch `change` again const newEvent = new Event('change', { bubbles: true }) event.target.dispatchEvent(newEvent); info(event) } function info (event) { console.log(event.type) } function getChar(event) { // will show X if letter, will show Y if Digit, otherwise Z return event.code.startsWith('Key') ? 'X' : event.code.startsWith('Digit') ? 'Y' : 'Z' }
Top comments (0)