JS IME composition and Mac accent menu handling in custom input field

192 Views Asked by At

Working on a project where handling various languages (Japanese, Chinese etc.) as well as handling different language directions (right-to-left, left-to-right and the combinations of the two) is crucial part of our system. Because of that we have a custom implementation of input field to do things consistently across browser spectrum. One particularly challenging thing to replicate (via events from W3C specification) is to emulate IME composition together with Mac accent menu.

IME composition itself is fine. Handling IME composition languages via compositionstart, compositionupdate, compositionend events and non-IME languages via input event works well (using event.isComposing for separation between the two).

But incorporating the Mac accent menu gets wild quite quickly. The problem is the lifecycle of the accent menu doesn't trigger the events one would expect. E.g. the initial "Click & hold" that opens the accent menu triggers the following sequence:

  1. keydown, event.isComposing = false
  2. input, event.isComposing = false
  3. keydown, event.isComposing = false (X-times based on how long the key is held)
  4. keyup, event.isComposing = false

Thus, although Mac accent menu behaves (visually) the same way as IME composition for various languages, the event chain doesn't recognise that at all. Which forces the implementation to hack it by having keydown/keyup counter to manually recognise this "IME-composition-without-composition-events" state.

Everything going forward makes things even more complicated. E.g. navigating via arrows triggers compositionstart and compositionupdate events but selecting character via number just calls input event.

There have to be several people who implemented similar logic in the past for various browser based editors so any tips or experience is appreciated.

1

There are 1 best solutions below

3
Kaiido On

You can check the inputType property of either the beforeinput or input event, to detect if you're still handling a composition event. In such a case you will receive "insertCompositionText" for each update (the first input of the non accented character would still be "insertText" though).

document.querySelector("input").oninput = (evt) => console.log(evt.data, evt.inputType);
<input>