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)