Phenomenon
In Firefox, when composing text with Korean IME, moving focus to another textbox causes the composition to not properly terminate.
Reproduction example
- Focus the first contenteditable element.
- Activate Korean IME.
- Start composing a character (e.g., ‘나’ = ㅎ + ㄴ).
- Before composition completes (after typing initial consonant ㅎ), click the second textbox or move focus using a button.
Observed behavior
- compositionend fires but jamo remains:
compositionendevent fires, but the jamo ‘ㅎ’ remains in the first field. - Second field focus: Focus moves to the second field.
- Selection instability: Firefox may also have selection-related issues, where the jamo in the first field may not be properly selected.
- Input disabled state: Attempting to type in the second field may cause the jamo in the first field to disappear or behave unexpectedly.
Expected behavior
- Composition should fully terminate and the final character (‘나’) should be committed to the first field.
- Focus should smoothly move to the second field.
- The cursor state in the first field should be cleaned up.
Notes and possible direction for workarounds
- Wait for compositionend: Wait for
compositionendevent to fully process before moving focus. - Selection API caution: Firefox’s selection API may behave differently from other browsers, so caution is needed.
- Use clearSelection: After moving focus, explicitly call
selection.removeAllRanges()to clean up existing selection.
Code example
let isComposing = false;
const editor1 = document.querySelector('div[contenteditable]:nth-child(1)');
const editor2 = document.querySelector('div[contenteditable]:nth-child(2)');
editor1.addEventListener('compositionstart', () => {
isComposing = true;
});
editor1.addEventListener('compositionend', () => {
isComposing = false;
// Firefox-specific: clear selection
setTimeout(() => {
document.getSelection()?.removeAllRanges();
editor2.focus();
}, 100);
});
// Safely move focus on button click
document.querySelector('button').addEventListener('click', () => {
if (!isComposing) {
editor2.focus();
} else {
alert('IME input in progress. Please wait for completion.');
}
});