关于输入法组合问题

关于输入法组合问题

·

1 min read

问题

最近几天回想起了以前遇到的一个问题,现在有一个按下回车发送聊天内容的功能,我想打一个英文,在输入法中打完后习惯性地按下回车,啪地一声,内容被发送出去了?!

正常来说,第一次按回车应该把“hello”这五个字母输入到框里,再次按下回车才会进行发送。

那么今天首先要做的是复现这个问题,然后再看如何避免。

复现失败

我先在 Codepen 上做了一个 Demo:

function App (props) {
  const inputRef = React.useRef()

  const handleKey = React.useCallback((ev) => {
    if (ev.keyCode !== 13) {
      return
    }
    console.log(ev)
  }, [])

  return (
    <div className="main">
      <input
        onKeyDown={handleKey}
        placeholder="1"
      />
    </div>
  )
}

测试了一下,按下回车,问题并没有复现。。。这里我还以为是 React 的问题,可能是 React 帮忙处理了,于是换了 vanilla 的写法:

React.useEffect(() => {
  inputRef.current.addEventListener('keydown', handleKey)
  return () => {
    inputRef.current.removeEventListener('keydown', handleKey)
  }
}, [])

但还是没有复现。。。

我再换了其他的事件去测试:

<input
  onKeyDown={handleKey}
  onKeyUp={handleKey}
  onKeyPress={handleKey}
  placeholder="1"
/>

emmm,还是没有复现。

最后换了不同的浏览器测试,依然没有。

找到原因

先说结论,这个问题和浏览器无关,和 React 也无关,和事件类型几乎无关。

也就是说,在各种浏览器上、React 上、键盘事件类型(keydownkeyup)上都是能复现的,只是我的写法有问题。(输入法下不会触发 keypress

复现的标准写法是:

if (ev.code === 'Enter') {
   // 必出问题
}

关键在事件的 code 属性上,而之前测试用的是 keyCode 所以没复现。

是这样的,每次触发 keydownkeyup 事件的时候,code 表示的当前按键的信息,不管在不在输入法状态,按下 Enter 的时候,code 就是 Enter,所以会出现在“在输入法里按回车直接发送”的情况。

keyCode 不一样,首先他返回的时候按键的编号,但他有个特殊编号 - 229出现这个编号的时候,就表示当前是输入法状态,换句话说,只要你当前在使用输入法输入,那么不管触发事件的是什么按键,keyCode 都将是 229。

可以看到,一开始输入的时候,按下“abcd”,keyCode 都是 229,表示在使用输入法;接着按下第一次 Enter,这时候依然是 229,同时将刚刚的文字放到了输入框里;最后再按下 Enter,keyCode 才变成 13。

所以如果直接用 keyCode === 13 来判断,就不会出现“提前发送”的问题。

compositionstart

composition 相关的事件也可以用来解决这个问题,如果一定要用 code 的话,就考虑 composition 事件吧,参考 Codepen 的 demo。