현상
On Android virtual keyboards, after selecting all text in a contenteditable element, typing a letter does not immediately start IME composition. The first letter is inserted as plain text, and IME composition only starts from second letter.
재현 예시
- Type text in contenteditable element (e.g., “こんにちは”).
- Press Ctrl+A to select all text.
- Type first letter (e.g., “あ”).
관찰된 동작
- First letter plain text: ‘あ’ is inserted as plain text (not part of IME composition)
- Selection remains: The “Select All” selection stays visible
- IME starts delayed: IME composition (candidate window) only appears after typing second letter
- Result: First letter appears as plain text, then IME composition replaces from second letter
예상 동작
- IME composition should start immediately when first letter is typed
- The entire selection should be replaced with IME composition
참고사항 및 가능한 해결 방향
- Clear selection before typing: Remove selection before user starts new input
- Detect and handle first letter separately: Check if plain text is inserted before composition starts
- Use beforeinput event: Monitor
inputType = 'insertText'to detect plain text insertion - User education: Add UI hint to clear selection before starting new IME input
코드 예시
const editor = document.querySelector('div[contenteditable]');
let imeStarted = false;
let wasSelectAll = false;
// Detect select all
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'a') {
wasSelectAll = true;
}
});
// Track composition start
editor.addEventListener('compositionstart', () => {
imeStarted = true;
// Clear selection if it exists
const selection = window.getSelection();
if (selection.rangeCount > 0 && !selection.isCollapsed) {
selection.removeAllRanges();
}
});
// Distinguish plain text vs IME
editor.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertText' && !imeStarted && wasSelectAll) {
// Plain text was inserted before IME started
console.warn('Plain text inserted before IME started:', e.data);
}
});
editor.addEventListener('keydown', (e) => {
if (wasSelectAll) {
// First letter after select all detected
console.log('First letter after select all:', e.key);
// Optionally add cleanup logic
}
});