케이스 ce-0213-ime-beforeinput-insertcompositiontext-input-deletecontentbackward-ko · 시나리오 scenario-beforeinput-input-inputtype-mismatch

beforeinput은 insertCompositionText로 발생하지만 input은 deleteContentBackward로 발생함

OS: iOS 17.0 기기: iPhone or iPad Any 브라우저: Safari 17.0 키보드: Korean (IME) 초안
compositionimebeforeinputinputinputtype-mismatchtargetrangesiossafari

현상

IME 조합 중 beforeinputinputType: 'insertCompositionText'로 발생하는 반면 해당 input 이벤트는 inputType: 'deleteContentBackward'로 발생할 수 있습니다. 이 불일치는 iOS Safari의 한글 IME뿐만 아니라 다양한 브라우저/IME 조합에서 발생할 수 있으며, Firefox의 특정 IME, 다른 모바일 브라우저 등에서도 관찰됩니다. 이 불일치는 input 이벤트의 inputType만 사용하여 DOM 변경을 올바르게 이해하는 것을 불가능하게 만듭니다.

재현 예시

  1. contenteditable 요소에 포커스를 둡니다.
  2. IME를 활성화합니다 (한글, 일본어, 중국어 또는 기타 언어).
  3. 텍스트 조합을 시작합니다 (예: 한글의 경우 “ㅎ” 그 다음 “ㅏ” 그 다음 “ㄴ”을 입력하여 “한” 조합).
  4. 조합을 업데이트하기 위해 계속 입력합니다 (예: 한글의 경우 “ㄱ” 그 다음 “ㅡ” 그 다음 “ㄹ”을 입력하여 “한글”로 업데이트).
  5. 브라우저 콘솔이나 이벤트 로그에서 beforeinputinput 이벤트를 관찰합니다.
  6. beforeinput.inputTypeinput.inputType과 일치하는지 확인합니다 - 다를 수 있습니다.

관찰된 동작

조합 텍스트를 업데이트할 때:

  1. beforeinput 이벤트:

    • inputType: 'insertCompositionText'
    • isComposing: true
    • data: '한글' (새 조합 텍스트)
    • getTargetRanges()는 조합 텍스트가 삽입될 위치를 나타내는 범위를 반환합니다
    • 범위는 일반적으로 교체될 이전 조합 텍스트를 포함합니다
  2. input 이벤트:

    • inputType: 'deleteContentBackward' (불일치!)
    • data: null 또는 비어 있음
    • 실제 DOM 변경은 beforeinput에서 나타난 삽입이 아닌 삭제일 수 있습니다
    • 조합 텍스트가 업데이트되는 대신 삭제될 수 있습니다
  3. 결과:

    • inputType에 의존하여 무슨 일이 일어났는지 결정하는 핸들러가 변경을 잘못 해석합니다
    • beforeinputtargetRanges가 손실되고 input에서 사용할 수 없습니다
    • 애플리케이션 상태가 DOM 상태와 일관되지 않을 수 있습니다

예상 동작

  • input 이벤트의 inputTypebeforeinput 이벤트의 inputType과 일치해야 합니다
  • beforeinputinsertCompositionText로 발생하면 inputinsertCompositionText를 가져야 합니다
  • input.databeforeinput.data와 일치해야 합니다 (또는 실제 커밋된 텍스트를 반영해야 함)
  • DOM 변경이 beforeinput에서 나타난 것과 일치해야 합니다

영향

이것은 다음을 일으킬 수 있습니다:

  • 잘못된 DOM 변경 감지: 핸들러가 삽입이 발생했다고 생각하지만 실제로는 삭제가 발생했습니다
  • 손실된 targetRanges 컨텍스트: beforeinputtargetRanges는 중요하지만 input에서 사용할 수 없습니다
  • 잘못된 실행 취소/다시 실행: 실행 취소/다시 실행 스택이 잘못된 작업 유형을 기록합니다
  • 상태 동기화 문제: 애플리케이션 상태가 일관되지 않게 됩니다
  • 이벤트 핸들러 실패: 일치하는 inputType 값을 기대하는 핸들러가 실패합니다

브라우저 비교

  • iOS Safari: beforeinput에서 insertCompositionText를 발생시키지만 input에서 deleteContentBackward를 발생시킬 수 있음, 특히 한글 및 일본어 IME에서 자주 발생
  • macOS Safari: 특정 IME 조합에서 유사한 불일치를 보일 수 있음
  • Firefox: 특정 IME 시나리오에서 불일치를 가질 수 있음, 특히 모바일 기기에서
  • Chrome/Edge: 일반적으로 이벤트 간 일관된 inputType이지만 엣지 케이스가 있을 수 있음
  • Android Chrome: 텍스트 예측 및 IME 변형으로 인해 불일치 가능성이 더 높음
  • 모바일 브라우저: 다양한 IME에서 일반적으로 불일치 가능성이 더 높음

참고 및 해결 방법 가능한 방향

  • beforeinput에서 targetRanges 저장: input 핸들러에서 사용하기 위해 targetRanges를 저장합니다:

    let lastBeforeInputTargetRanges = null;
    let lastBeforeInputType = null;
    
    element.addEventListener('beforeinput', (e) => {
      lastBeforeInputTargetRanges = e.getTargetRanges?.() || [];
      lastBeforeInputType = e.inputType;
    });
    
    element.addEventListener('input', (e) => {
      if (lastBeforeInputType && e.inputType !== lastBeforeInputType) {
        // 불일치 감지 - targetRanges를 사용하여 실제 변경 이해
        if (lastBeforeInputTargetRanges && lastBeforeInputTargetRanges.length > 0) {
          // inputType이 아닌 targetRanges를 기반으로 처리
          handleActualChange(lastBeforeInputTargetRanges, e);
        }
      }
      lastBeforeInputTargetRanges = null;
      lastBeforeInputType = null;
    });
  • DOM 상태 비교: 불일치가 발생하면 실제 변경을 이해하기 위해 이전과 이후 DOM을 비교합니다:

    let domBefore = null;
    
    element.addEventListener('beforeinput', (e) => {
      domBefore = element.innerHTML;
    });
    
    element.addEventListener('input', (e) => {
      const domAfter = element.innerHTML;
      if (lastBeforeInputType && e.inputType !== lastBeforeInputType) {
        // domBefore와 domAfter를 비교하여 실제 변경 이해
        const actualChange = compareDOM(domBefore, domAfter);
        handleChange(actualChange);
      }
      domBefore = null;
    });
  • inputType에만 의존하지 않기: 조합 이벤트를 처리할 때 항상 DOM 검사로 확인합니다

  • 우아하게 처리: inputType 일치에 의존하지 않는 폴백 로직을 가집니다

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: Korean (IME)
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.