中文输入法 composition event 在 chrome 和 safari 表现相反

What

在开发编辑输入组件的时候,如果输入的是中文,监听 delete 事件会删除输入框里已经写好的文案,期望效果是删除正在输入的拼音。

bug 复现如下

期望效果

Why

composition 有几个事件

  • compositionstart
  • compositionupdate
  • compositionend

具体示例 MDN 上给了一个 demo 可以体验

让我们看下中文输入 asd,然后 delete 后,发生了什么

chrome 事件顺序如下

  • delete

  • onCompositionUpdate

  • delete

  • onCompositionUpdate

  • delete

  • onCompositionUpdate

  • onCompositionEnd

safari 事件顺序如下

  • onCompositionUpdate

  • delete

  • onCompositionUpdate

  • delete

  • onCompositionEnd

  • delete

如果输入法正在 composition,我们是知道的,只需要监听 compositionstart, compositionupdate 打个标记即可,然后在 delete 拦截掉

function onCompositionStart() {
  isOnComposition = true
}
function onCompositionUpdate() {
  isOnComposition = true
}
function onCompositionEnd() {
  isOnComposition = false
}

function mockDelete() {
  // 拦截,false 的时候,删除业务 tag
  if (!isOnComposition && isMultipleMode && tags.value?.length && !phone.value) {
    const lastTag = tags.value[tags.value?.length - 1]
    removeTag(lastTag?._id)
  }
}

目前看没啥问题,但是到最后一个字符 safari 就和 chrome 表现不一样了,

因为 compositonend 在前 delete 在后,这就导致 safari 在监听到 delete 的时候,isOnComposition 已经被置为 false 了,这就导致会同时删除中文拼音以及业务 tag

解决方法也简单,在 compositionend 继续打个标记

function onCompositionEnd() {
  isOnComposition = false
  mockDelete.safariBug = true
}

function mockDelete() {
  // safari bug: compositionEnd => delete
  if (isSafari && mockDelete.safariBug) {
    return mockDelete.safariBug = false
  }  
}