codemirror ios拼音预输入带来的问题

codemirror


发表于 2018-12-31 15:25


好不容易解决了codemirror ios中文输入的问题(请见 https://www.qyh.me/space/web/article/codemirror-ios-input-chinese-bug),但很快又发现了新的问题,iso的默认中文输入法存在拼音的预输入问题(这本身并不是一个问题,但会带来其他的问题,见下面视频):

也就是按退格键的时候会出现删除一个字符后,输入法丢失了中文联想,并且再次摁退格键的时候光标会丢失,我们期待的效果应该是这样的:

这个问题严重的影响了用户体验,所以必须得解决。

一番调试之后,我发现问题出在了handleKeyBinding这个方法上:

  function handleKeyBinding(cm, e) {
    var name = keyName(e, true);
    if (!name) { return false }
	
    if (e.shiftKey && !cm.state.keySeq) {
      // First try to resolve full name (including 'Shift-'). Failing
      // that, see if there is a cursor-motion command (starting with
      // 'go') bound to the keyname without 'Shift-'.
      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
          || dispatchKey(cm, name, e, function (b) {
               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
                 { return doHandleBinding(cm, b) }
             })
    } else {
      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
    }
  }

当我把handleKeyBinding方法改为如下后:

 function handleKeyBinding(cm, e) {
     ......
    if (e.shiftKey && !cm.state.keySeq) {
    } else {
	  if(e.keyCode == 8 && ios){
	    return false;
	  }
      ......
    }
  }

发现ios端就能正常工作了,虽然如此,但我一直对这种方法心有余悸:

  1. 如果是安卓端的输入法存在拼音预输入,那么会不会也有这个问题
  2. 直接返回false,会不会导致一些关键的步骤被省略掉

今天使用pc端输入的时候发现,win10自带的输入也是这种效果,但为什么pc端没有这个问题呢?难道codemirror针对pc端这种问题做了额外的处理?如果它做了额外的处理,那么我根据它做的额外处理再运用到ios不也解决了问题了吗?

进行了几遍调试之后我发现,codemirror并没有针对pc端做额外的处理,之所以pc端没有这样的问题,是因为退格键的keyCode发生了改变,在英文输入状态下摁退格键,发现它的事件如下:
base64_b0b192d69d364f71b1f2173f519792f0.png
退格键的keyCode为8,但是在中文输入状态下,退格键事件如下:
base64_0bd3aea6039840209f3e2b075eb9cbb2.png
发现退格键居然变成了229,回到handleKeyBindingkeyName方法,可以看到229是没有对应的keyName的:

function keyName(event, noShift) {
    if (presto && event.keyCode == 34 && event["char"]) { return false }
    var name = keyNames[event.keyCode];
    if (name == null || event.altGraphKey) { return false }
    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
    if (event.keyCode == 3 && event.code) { name = event.code; }
    return addModifierNames(name, event, noShift)
  }

所以摁这种退格键时handleKeyBinding方法直接返回了false,没有后续操作了,这就解决了我的第二个顾虑。这样就很容易想到,ios之所以出现这个问题,是因为拼音预先输入的时候,摁退格键,它的keyCode还是8,既然keyCode没变,那么只能比对下两个事件,看看是否存在可以用来判断的属性。

最后我把目光放到了isComposing这个属性上,

The KeyboardEvent.isComposing read-only property returns a Boolean value indicating if the event is fired after compositionstart and before compositionend.

查了下这个解释,总是感觉通过这个属性来判断并不靠谱,但总比通过ios来判断好的多,最后我把keyName方法改成了这样:

 function keyName(event, noShift) {
    if(event.isComposing && event.keyCode == 8){
        return false;
    }
    if (presto && event.keyCode == 34 && event["char"]) { return false }
    var name = keyNames[event.keyCode];
    if (name == null || event.altGraphKey) { return false }
    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
    if (event.keyCode == 3 && event.code) { name = event.code; }
    return addModifierNames(name, event, noShift)
  }

虽然解决了ios下拼音预输入的问题,但总感觉不是很靠谱,不过好在在codemirror6中,作者声称手机端的体验会得到极大的改善,到时候再看下他们时如何面对或者避免这个问题的。

2019.1.4

今天无意中看到一篇文章:enhance-ime-support-of-codemirror-with-composition-events,这篇文章详细的介绍了Composition Event的几个事件,原来通过它们可以监听输入法的组字模式,同时文章中提到:

在組字模式下,不管你鍵盤按什麼按鍵,JS 的 Keyboard Event 都只拿得到 keyCode = 229──他根本不知道你在幹嘛

这解释了为什么pc端键盘退格keyCode变成了229,回过头来再看下codemirror的源码,原来它早就对这三个事件做了处理:

on(div, "compositionstart", function (e) {
      this$1.composing = {data: e.data, done: false};
    });
    on(div, "compositionupdate", function (e) {
      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
    });
    on(div, "compositionend", function (e) {
      if (this$1.composing) {
        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
        this$1.composing.done = true;
      }
    });

那么我只要通过判断cm.display.input.composing是否有值不就可以判断它是否在组字模式了吗?所以我最后改成了这样:

 function handleKeyBinding(cm, e) {
  if(cm.display.input.composing){
      return false;
  }
   ......
}      

搜索