Scenario

IME composition causes duplicate text in headings (WebKit only)

When using IME to input CJK text in heading elements (H1, H2, etc.) in WebKit browsers, pressing Space to confirm composition causes both the raw Pinyin buffer AND the confirmed characters to appear together.

ime
Scenario ID
scenario-ime-duplicate-text-heading

Details

When composing text with an IME (Input Method Editor) for Chinese or other CJK languages inside heading elements (<h1>, <h2>, etc.) in WebKit browsers, pressing Space to confirm the composition causes both the raw input buffer (e.g., Pinyin “nihao”) and the confirmed characters (e.g., “你好”) to appear together in the editor.

Problem Description

The issue occurs specifically when:

  1. User is in a heading element (H1-H6)
  2. Using Chinese IME (or other CJK IME that shows raw input buffer)
  3. Types Pinyin (e.g., “nihao” for “你好”)
  4. Presses Space to confirm the composition

Expected Behavior

  • Only the confirmed Chinese characters (“你好”) should appear in the heading
  • Raw Pinyin buffer should NOT be visible

Actual Behavior (WebKit Bug)

  • Both raw Pinyin (“nihao”) AND confirmed characters (“你好”) appear together
  • Result: “nihao 你好” instead of just “你好”

Affected Browsers

  • Safari (WebKit) - Issue confirmed
  • Chrome (Blink/WebKit) - Issue may occur in some versions
  • Firefox - Does NOT exhibit this behavior (works correctly)

Affected Languages

  • Chinese IME (shows Pinyin buffer during composition)
  • Other CJK IMEs that display raw input buffer during composition

Root Cause

WebKit’s IME composition handling in heading elements appears to incorrectly maintain both:

  1. The raw input buffer being composed
  2. The confirmed/composed characters

When composition is confirmed with Space key, WebKit fails to properly clear the raw buffer from the DOM in heading elements.

Workarounds

  1. Use paragraphs instead of headings:

    • Replace <h2> with <p><strong> or use CSS styling instead of semantic headings
    • Composition works correctly in non-heading elements
  2. Force DOM cleanup after composition:

    heading.addEventListener('compositionend', () => {
      setTimeout(() => {
        // Remove any raw Pinyin text that shouldn't be there
        const text = heading.textContent.replace(/[a-z]+/g, '');
        heading.textContent = text;
      }, 0);
    });
  3. Intercept Space key during composition:

    heading.addEventListener('keydown', (e) => {
      if (isComposing && e.key === ' ') {
        e.preventDefault();
        // Let IME handle composition completion naturally
      }
    });

References

Scenario flow

Visual view of how this scenario connects to its concrete cases and environments. Nodes can be dragged and clicked.

React Flow mini map

Variants

Each row is a concrete case for this scenario, with a dedicated document and playground.

Case OS Device Browser Keyboard Status
ce-0233-chinese-ime-pinyin-heading-chrome-en Windows 10/11 Desktop Any Chrome 120+ Chinese (IME) - Windows Chinese Input draft

Cases

Open a case to see the detailed description and its dedicated playground.

Related Scenarios

Other scenarios that share similar tags or category.

Tags: composition, ime, webkit

insertParagraph preventDefault breaks IME composition state in Safari

In Safari desktop, when preventDefault() is called on keydown or beforeinput events for insertParagraph (Enter key), the IME composition state becomes corrupted. Subsequent text input fails to trigger proper input events, causing characters to not be inserted or composition to malfunction.

3 cases
Tags: composition, ime

beforeinput and input events have different inputType values

During IME composition or in certain browser/IME combinations, the beforeinput event may have a different inputType than the corresponding input event. For example, beforeinput may fire with insertCompositionText while input fires with deleteContentBackward. This mismatch can cause handlers to misinterpret the actual DOM change and requires storing beforeinput's targetRanges for use in input event handling.

1 case
Tags: composition, ime

Selection mismatch between beforeinput and input events

The selection (window.getSelection()) in beforeinput events can differ from the selection in corresponding input events. This mismatch can occur during IME composition, text prediction, or when typing adjacent to formatted elements like links. The selection in beforeinput may include adjacent formatted text, while input selection reflects the final cursor position.

1 case
Tags: composition, ime

getTargetRanges() returns empty array in beforeinput events

The getTargetRanges() method in beforeinput events may return an empty array or undefined in various scenarios, including text prediction, certain IME compositions, or specific browser/device combinations. When getTargetRanges() is unavailable, developers must rely on window.getSelection() as a fallback, but this may be less accurate.

1 case

Comments & Discussion

Have questions, suggestions, or want to share your experience? Join the discussion below.