์ผ€์ด์Šค ce-0293-ios-dictation-duplicate-events-safari-ko ยท ์‹œ๋‚˜๋ฆฌ์˜ค scenario-ios-dictation-duplicate-events

iOS dictation re-fires input events with text split into words

OS: iOS 17.0+ ๊ธฐ๊ธฐ: iPhone or iPad Any ๋ธŒ๋ผ์šฐ์ €: Safari 17.0+ ํ‚ค๋ณด๋“œ: Voice Dictation ์ดˆ์•ˆ
dictationvoice-inputbeforeinputinputiossafariduplicate-eventssync-issue

Phenomenon

On iOS Safari, when using voice dictation to input text into contenteditable elements, the system fires initial beforeinput and input events with the complete dictated text. After the initial input completes, the system re-fires beforeinput and input events with the text split into individual words, causing event handlers to execute multiple times for the same input.

Reproduction example

  1. Open a web page with a contenteditable element on iOS Safari (iPhone or iPad).
  2. Focus the contenteditable element.
  3. Activate voice dictation (long press spacebar or tap microphone icon on keyboard).
  4. Dictate text: โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ (or any multi-word phrase).
  5. Observe the beforeinput and input events in the browser console or event log.

Observed behavior

Initial Dictation Sequence

  1. User activates dictation and speaks โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€
  2. beforeinput event fires with:
    • inputType: 'insertText'
    • data: '๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค'
    • isComposing: false
  3. input event fires with the complete text โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ inserted into DOM

Duplicate Events Sequence (Bug)

  1. After a short delay (typically 100-500ms), beforeinput event fires again with:
    • inputType: 'insertText'
    • data: '๋งŒ๋‚˜์„œ'
    • isComposing: false
  2. input event fires with โ€œ๋งŒ๋‚˜์„œโ€ inserted
  3. beforeinput event fires again with:
    • inputType: 'insertText'
    • data: ' ' (space character)
    • isComposing: false
  4. input event fires with space inserted
  5. beforeinput event fires again with:
    • inputType: 'insertText'
    • data: '๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค'
    • isComposing: false
  6. input event fires with โ€œ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ inserted

Key Characteristics

  • Composition events (compositionstart, compositionupdate, compositionend) do NOT fire during dictation
  • isComposing is always false in all events
  • Events are re-fired after the initial input completes
  • Text is split at word boundaries (spaces)
  • The DOM state after duplicate events is the same as after initial events (no actual change)
  • Event sequence becomes out of sync with DOM state

Expected behavior

  • Initial beforeinput and input events should fire once with the complete dictated text
  • Events should NOT be re-fired after completion
  • If events are re-fired, they should reflect actual DOM changes (not duplicate insertions)
  • Event sequence should remain synchronized with DOM state
  • Composition events should fire during dictation (as they do on macOS Safari)

Impact

This can lead to:

  • Duplicate processing: Event handlers execute multiple times for the same input
  • State synchronization issues: Application state may become inconsistent with DOM state
  • Performance issues: Unnecessary processing of duplicate events
  • Undo/redo corruption: Undo stack may contain duplicate or incorrect entries
  • Validation issues: Validation logic may run multiple times on the same input
  • Formatting issues: Formatting logic may be applied incorrectly due to split text
  • Event sequence confusion: Handlers expecting a single input event receive multiple events

Browser Comparison

  • iOS Safari: Dictation does not fire composition events, events are re-fired after completion with text split into words
  • iOS Chrome: Same behavior as Safari (uses WebKit engine, required by Apple)
  • macOS Safari: Dictation fires composition events, events are not re-fired after completion
  • Chrome/Edge/Firefox (Desktop): Dictation behavior varies but generally more consistent, no duplicate re-firing

Distinguishing Dictation Input

Important: There is no reliable way to detect dictation input in web applications on iOS. Web APIs do not provide dictation detection capabilities, and native iOS APIs like UITextInputContext.isDictationInputExpected are not available in web contexts.

Potential Indicators (Not Reliable)

  • Absence of composition events (but this also occurs with Korean IME on iOS)
  • Rapid insertion of multiple words
  • Text appears to be split and re-inserted
  • Events fire in quick succession with complete words
  • isComposing is always false (but this is also true for Korean IME on iOS)

These indicators are not definitive and may produce false positives.

Event Sequence

The sequence of events when inputting โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ via dictation:

Phase 1: Initial Dictation Input

OrderEventinputTypedataDOM State (before)DOM State (after)
1beforeinputinsertTextโ€™๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€™""-
2inputinsertTextโ€™๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€™"""๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ โœ…

Phase 2: Duplicate Events (Bug)

After initial input completes, after a delay of approximately 100-500ms, events are re-fired with text split into words:

OrderEventinputTypedataDOM State (before)DOM State (after)
3beforeinputinsertTextโ€™๋งŒ๋‚˜์„œโ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€-
4inputinsertTextโ€™๋งŒ๋‚˜์„œโ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค""๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ โŒ
5beforeinputinsertTextโ€™ โ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€-
6inputinsertTextโ€™ โ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค""๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ โŒ
7beforeinputinsertTextโ€™๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€-
8inputinsertTextโ€™๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€˜โ€œ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค""๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹คโ€ โŒ

Key Characteristics

  • Events 1-2: Complete text inserted at once (DOM actually changes)
  • Events 3-8: Text re-fired word-by-word but DOM doesnโ€™t change
  • Composition events: compositionstart, compositionupdate, compositionend events do NOT fire in any phase
  • isComposing: All events have isComposing: false
  • Delay between phases: 100-500ms delay between Event 2 and Event 3

Complete Event Monitoring

Code to monitor all events during iOS dictation input:

const element = document.querySelector('[contenteditable]');
const eventLog = [];

const eventsToMonitor = [
  'compositionstart', 'compositionupdate', 'compositionend',
  'beforeinput', 'input',
  'keydown', 'keyup', 'keypress'
];

eventsToMonitor.forEach(eventType => {
  element.addEventListener(eventType, (e) => {
    const eventData = {
      timestamp: Date.now(),
      type: eventType,
      inputType: e.inputType || null,
      data: e.data || null,
      isComposing: e.isComposing || false,
      textContent: element.textContent
    };
    eventLog.push(eventData);
    console.log(`[${eventType}]`, eventData);
  }, { capture: true });
});

Events That Fire vs Do Not Fire

Event TypeFires?Initial InputDuplicate Events
beforeinputโœ… Yes1 time3 times
inputโœ… Yes1 time3 times
compositionstartโŒ No--
compositionupdateโŒ No--
compositionendโŒ No--
keydownโŒ No--
keyupโŒ No--
keypressโŒ No--

Notes and possible direction for workarounds

Event Handling Considerations

  • Event handlers may execute multiple times for the same input
  • Events without actual DOM changes (Events 4, 6, 8) should not be processed
  • Check textContent to determine if DOM actually changed

Undo/Redo Stack

  • Recording duplicate events in undo stack creates duplicate undo entries
  • Only record in undo stack when thereโ€™s an actual DOM change

Additional Considerations

Selection State

  • Selection state may be reset when duplicate events fire, so donโ€™t use selection for duplicate detection; trust only textContent

Undo/Redo Stack

  • Recording duplicate events in the undo stack creates duplicate undo entries
  • Using textContent-based deduplication ensures only actual changes are recorded in the undo stack

Voice Control Simultaneous Use

  • Enabling both Voice Control and Dictation in iOS settings may cause text to be inserted twice
  • This case actually changes the DOM, so textContent-based deduplication wonโ€™t detect it
  • Recommend users enable only one

Test Environment

iOS VersionBrowserLanguageReproduced
iOS 16.xSafariKoreanโœ… Confirmed
iOS 16.xSafariEnglishโœ… Confirmed
iOS 16.xChrome iOSKoreanโœ… Confirmed
iOS 17.xSafariKoreanโœ… Confirmed
iOS 17.xSafariEnglishโœ… Confirmed
iOS 17.xChrome iOSKoreanโœ… Confirmed
iOS 18.xSafariKoreanโš ๏ธ Unconfirmed
iOS 18.xSafariEnglishโš ๏ธ Unconfirmed

Note: The same issue likely occurs across all iOS versions (shared WebKit engine). Issue appears to occur regardless of language.

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ๋ณ€ํ˜•

์ผ€์ด์Šค OS ๋ธŒ๋ผ์šฐ์ € ์ƒํƒœ
ce-0293-ios-dictation-duplicate-events-safari-ko iOS 17.0+ Safari 17.0+ ์ดˆ์•ˆ
ce-0294-ios-dictation-duplicate-events-safari-ko iOS 17.0+ Safari 17.0+ ์ดˆ์•ˆ

Playground for this case

Use the reported environment as a reference and record what happens in your environment while interacting with the editable area.

Reported environment
OS: iOS 17.0+
Device: iPhone or iPad Any
Browser: Safari 17.0+
Keyboard: Voice Dictation
Your environment
Sample HTML:
Event log
Use this log together with the case description when filing or updating an issue.
0 events
Interact with the editable area to see events here.