Jan 3, 2014
DOM Composition Events compatibility notes

If you don’t frequently type in a non-latin language, e.g. Chinese, then most likely you’ve never heard of Composition Events. Even for a Chinese developer, it’s rare that these events are needed in development. In fact I didn’t learn about their existence until recently, when I was trying to alter an input field’s value as the user types. Everything works perfectly when typing English letters, but it turns into a mess when I tried Chinese. Let me explain.
The way IME composition works is that it buffers the user’s keystrokes until a character/word selection has been made, finalizing the input. The buffered keystrokes are temporarily displayed, but not actually inserted into the DOM. However, when I change the input field’s value during a composition, the composition gets terminated early and these buffered keystrokes get inserted into the input field.
As I was banging my head against the wall trying to find a solution using key events and async updates, I discovered composition events. They are exactly what I need! But not before I get bitten by the various inconsistencies across major browsers. According to the spec, this is the expected behvaior:
- fire
compositionstartwhen composition starts. - fire
compositionendwhen user selects a character/word and finalizes the input. - fire
compositionupdateon every input during composition, including immediately after the start event and immediately before the end event. inputevents should fire after composition events
What’s actually implemented in major mordern browsers:
- Firefox 26 : working according to the spec.
- Chrome 31 / Safari 7 : doesn’t fire
compositionupdateimmediately after the start event or before the end event. Forcompositionstart, the event’sdatavalue is wrong (undefinedwhere an empty string is expected). - IE10 : fires only one
compositionupdateright aftercompositionstart, then no more. - IE9 : fires
inputbefore composition events.
Pretty annoying, but luckily not too complicated to work around with for my case. All I had to do was listening for compositionstart and compositionend and avoid changing the input field’s value during the whole composition - and voila, problem solved!


