skip to Main Content

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


  1. 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 the input 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.

    const inputElement = document.getElementById('myInput');
    
    inputElement.addEventListener('keydown', function(event) {
      if (event.key === 'a') {
        event.preventDefault();
        document.execCommand('insertText', false, 'b');
      }
    });
    <label for="myInput">Type 'a' to be replaced with 'b': </label>
    <input type="text" id="myInput">

    UPDATE (2024): execCommand is deprecated. You can still try to save/restore caret position using inputElement.selectionStart, inputElement.selectionEnd and inputElement.setSelectionRange

    Login or Signup to reply.
  2. I accomplished something similar for my app by:

    1. Intercepting the keydown event.
    2. Manually adjusting the value of the input.
    3. Calling event.preventDefault() only if the input was manually adjusted (otherwise let the default behavior occur.)

    Live demo: https://bangtastic.vercel.app/

    • If space is pressed, it is turned into ! in certain cases.
    • Otherwise input is accepted as normal
    • In the linked code, value is bound to the input’s value.

    Note: this must happen on keydown; other events like input and keyup are too late.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search