let’s say I have an html input and I would like that whenever someone press a key or combination of keys while the input is focused, it input a different character where the caret is positioned.
for instance let’s say I have a site with virtual keyboard French that is compatible with the English keyboard. But I also give the user the ability to use custom keyboard shortcut to input French characters (those with accent let’s say).
How would one do that using ES6’s dispatchEvent & KeyboardEvent Object?
Here is what I tried (the code uses JIS Layout Keyboard):
let isShiftPressed = false;
let isControlPressed = false;
let isNonConvertPressed = false;
let isConvertPressed = false;
const kbdDown = function(event) {
if (event.code === "ShiftLeft" || event.code === "ShiftRight") {
isShiftPressed = true;
}
if (event.code === "ControlLeft" || event.code === "ControlRight") {
isControlPressed = true;
}
if (event.code === "NonConvert" || event.code === "NonConvert") {
isNonConvertPressed = true;
}
console.log("{ code : " + event.code + ", key : " + event.key + "}");
if (event.code === "KeyA" && isShiftPressed && isNonConvertPressed) {
event.preventDefault();
const keyPressedEvent = new KeyboardEvent("keydown", {
key: "X",
code: "KeyX"
})
event.target.dispatchEvent(keyPressedEvent);
}
}
const kbdUp = function(event) {
if (event.code === "ShiftLeft" || event.code === "ShiftRight") {
isShiftPressed = false;
}
if (event.code === "ControlLeft" || event.code === "ControlRight") {
isControlPressed = false;
}
if (event.code === "NonConvert" || event.code === "NonConvert") {
isNonConvertPressed = false;
}
}
const registerkbdHandler = function(element) {
element.addEventListener("keydown", kbdDown);
element.addEventListener("keyup", kbdUp);
}
registerkbdHandler(document.querySelector("input"));
<input type="text">
When I focus on the input field and press NonConvert + Shift + A it outputs the following on the browser console:
output-listing : console
{ code : NonConvert, key : NonConvert} script.js:21
{ code : ShiftLeft, key : Shift} script.js:21
{ code : KeyA, key : A} script.js:21
{ code : KeyX, key : X}
Clearly the keyboard event is getting dispatched but the input text field doesn’t show any new character.
2
Answers
At first I thought saving and restoring caret position might work. It did but my final solution using
document.execCommand
is better because it mimics actual typing, instead of programmatically changing value of theinput
element. This would allow you even to ctrl+z.The event used is
keydown
which is cancelable and also makes sense because you want the typing to be changed.This example replaces ‘a’ with ‘b’ but don’t forget to add your logic for detecting control keys and combinations.
UPDATE (2024):
execCommand
is deprecated. You can still try to save/restore caret position usinginputElement.selectionStart
,inputElement.selectionEnd
andinputElement.setSelectionRange
I accomplished something similar for my app by:
keydown
event.event.preventDefault()
only if the input was manually adjusted (otherwise let the default behavior occur.)Live demo: https://bangtastic.vercel.app/
!
in certain cases.value
is bound to the input’s value.Note: this must happen on
keydown
; other events likeinput
andkeyup
are too late.