关于输入法组合问题
Table of contents
问题
最近几天回想起了以前遇到的一个问题,现在有一个按下回车发送聊天内容的功能,我想打一个英文,在输入法中打完后习惯性地按下回车,啪地一声,内容被发送出去了?!
正常来说,第一次按回车应该把“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 上、键盘事件类型(keydown
、keyup
)上都是能复现的,只是我的写法有问题。(输入法下不会触发 keypress
)
复现的标准写法是:
if (ev.code === 'Enter') {
// 必出问题
}
关键在事件的 code
属性上,而之前测试用的是 keyCode
所以没复现。
是这样的,每次触发 keydown
、keyup
事件的时候,code
表示的当前按键的信息,不管在不在输入法状态,按下 Enter 的时候,code
就是 Enter,所以会出现在“在输入法里按回车直接发送”的情况。
但 keyCode
不一样,首先他返回的时候按键的编号,但他有个特殊编号 - 229
,出现这个编号的时候,就表示当前是输入法状态,换句话说,只要你当前在使用输入法输入,那么不管触发事件的是什么按键,keyCode
都将是 229。
可以看到,一开始输入的时候,按下“abcd”,keyCode
都是 229,表示在使用输入法;接着按下第一次 Enter,这时候依然是 229,同时将刚刚的文字放到了输入框里;最后再按下 Enter,keyCode
才变成 13。
所以如果直接用 keyCode === 13
来判断,就不会出现“提前发送”的问题。
compositionstart
composition 相关的事件也可以用来解决这个问题,如果一定要用 code
的话,就考虑 composition 事件吧,参考 Codepen 的 demo。