Phenomenon
In iOS Safari with Korean IME, the isComposing flag is always false in beforeinput and input events, and composition events (compositionstart, compositionupdate, compositionend) do not fire. This breaks the standard pattern of detecting composition state to allow browser default behavior for keyboard handlers (Enter, Backspace, Delete). Editors that override keyboard handlers must always allow browser default behavior in iOS Safari.
Reproduction example
- Create a
contenteditableelement with custom keyboard handlers for Enter, Backspace, Delete. - Focus the element in iOS Safari (iPhone/iPad).
- Activate Korean IME.
- Start composing Korean text (e.g., type “ㅎ” then “ㅏ” then “ㄴ” to compose “한”).
- While composition is active, press Enter, Backspace, or Delete.
- Observe that
isComposingisfalseinbeforeinputevents. - Observe that composition events do not fire.
- Custom keyboard handlers think composition is not active and prevent default.
- This breaks composition behavior.
Observed behavior
When pressing Enter/Backspace/Delete during Korean composition:
-
isComposing is always false:
beforeinputevents haveisComposing: falseinputevents haveisComposing: false- Cannot detect composition state using
isComposingflag
-
Composition events do not fire:
compositionstartdoes NOT firecompositionupdatedoes NOT firecompositionenddoes NOT fire- Cannot detect composition state using composition events
-
Custom keyboard handlers break composition:
- Handlers check
isComposing→ alwaysfalse - Handlers think composition is not active
- Handlers prevent default and handle keys customly
- This interferes with browser’s composition handling
- Composition text may be lost or incorrectly handled
- Handlers check
-
Result:
- Enter during composition may break composition or insert line break incorrectly
- Backspace during composition may delete incorrectly
- Delete during composition may delete incorrectly
- Composition behavior is broken
Expected behavior
isComposingflag should accurately reflect composition state- Composition events should fire during composition
- Custom keyboard handlers should be able to detect composition state
- Browser default behavior should be allowed during composition
- Keyboard handlers should not interfere with composition
Impact
- Broken composition: Custom keyboard handlers interfere with IME composition
- Lost text: Composition text may be lost or incorrectly handled
- Incorrect behavior: Enter/Backspace/Delete may not work as expected during composition
- Platform-specific bugs: Code that works on other browsers fails on iOS Safari
- IME-specific bugs: Code that works with other IMEs fails with Korean IME on iOS Safari
- Editor compatibility: Editors that rely on
isComposingor composition events break on iOS Safari
Browser Comparison
- iOS Safari (Korean IME):
isComposingis alwaysfalse, composition events do NOT fire - iOS Safari (Japanese/Kanji IME):
isComposingis accurate, composition events fire - Desktop Safari:
isComposingis accurate, composition events fire - Chrome/Edge:
isComposingis accurate, composition events fire - Firefox:
isComposingis accurate, composition events fire
Notes and possible direction for workarounds
-
Always allow browser default in iOS Safari: Do not prevent default for keyboard handlers in iOS Safari:
const isIOSSafari = /iPhone|iPad|iPod/.test(navigator.userAgent) && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent); element.addEventListener('beforeinput', (e) => { // iOS Safari: Always allow browser default for keyboard handlers if (isIOSSafari) { if (e.inputType === 'insertParagraph' || e.inputType === 'deleteContentBackward' || e.inputType === 'deleteContentForward') { return; // Allow browser default } } // Standard browsers: Check isComposing if (e.isComposing) { return; // Let browser handle it } // Custom keyboard handling if (e.inputType === 'insertParagraph') { e.preventDefault(); handleCustomEnter(); } }); -
Detect iOS Safari Korean IME pattern: Recognize
deleteContentBackward+insertTextpattern:let lastDeleteBackward = null; const isIOSSafari = /iPhone|iPad|iPod/.test(navigator.userAgent) && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent); element.addEventListener('beforeinput', (e) => { if (isIOSSafari) { // iOS Safari Korean IME pattern if (e.inputType === 'deleteContentBackward') { lastDeleteBackward = e; return; // Allow browser default } if (e.inputType === 'insertText' && lastDeleteBackward) { lastDeleteBackward = null; return; // Allow browser default (composition update) } // Conservative: allow browser default for all keyboard handlers if (e.inputType === 'insertParagraph' || e.inputType === 'deleteContentBackward' || e.inputType === 'deleteContentForward') { return; // Allow browser default } } // Standard browsers if (e.isComposing) { return; } // Custom handling // ... }); -
Do not rely on isComposing in iOS Safari: For iOS Safari Korean IME, do not use
isComposingflag to detect composition -
Do not rely on composition events in iOS Safari: For iOS Safari Korean IME, do not use composition events to detect composition
-
Use platform detection: Detect iOS Safari and apply special handling
-
Use beforeinput instead of keydown:
beforeinputevents provide better composition state information thankeydownevents