背景
AI聊天输入框场景下,我们通常需要支持用户按下enter回车的快捷发送,通过键盘事件实现
html
<textarea
@keydown="onInputKeydown"
placeholder="请输入内容,按 Enter 发送,Shift + Enter 换行"
></textarea>
js
// 回车发送
const onInputKeydown = (event) => {
if(event.key === "Enter" || event.keyCode === '13') {
if(event.shiftKey) return; // shift + enter 仅换行
event.preventDefault();
sendMessage(); // 发送处理
}
}
问题
但实际上enter作为确认操作,除了确认内容发送动作,还有确认输入法输入的动作;
如用户在输入法输入状态下,按下enter期望确认输入内容到文本框,此时也会触发上面的发送逻辑导致内容内发送出去。
我们可以 onInputKeydown 加一个合成状态(isComposing)判断
js
const onInputKeydown = (event) => {
if(event.key === "Enter" || event.keyCode === '13') {
if(event.shiftKey) return;
if(event.isComposing) return; // 此次为输入法状态下的enter(即输入法合成状态),不发送内容
event.preventDefault();
sendMessage();
}
}
这确实有效,但是在Safari下键盘输入文字(如输入英文)且在输入法合成状态,按下enter内容依旧被发送了!原因在于 event.isComposing 在Safari并不可信,导致不可用;
延展
那么解决Safari上这个问题,我们必须Safari上实现可信的isComposing;
尝试通过监听 composotionstart/compositionend 实现自己的 Composing 状态,但事实是不可行(Safari的composition事件与keydown、input等事件执行顺序问题);
但是input事件有 inputType 可判断到输入法状态 MDN inputType。
最终实现:额外监听input事件(先于keydown事件),针对Safari使用自己实现的Composing状态判断是否为输入法合成下enter
html
<textarea
@input="onInput"
@keydown="onInputKeydown"
placeholder="请输入内容,按 Enter 发送,Shift + Enter 换行"
></textarea>
js
// 引入浏览器检测库
import { UAParser } from 'ua-parser-js';
const parser = new UAParser();
const result = parser.getResult();
// 回车发送
const onInputKeydown = (event) => {
if(event.key === "Enter" || event.keyCode === '13') {
if(event.shiftKey) return;
// 针对safari浏览器兼容
if(result.browser.name === "safari") {
if(isCompositionInputType) { // 使用自己实现的Composing状态
isCompositionInputType = false;
return;
}
}else {
if(event.isComposing) return;
}
event.preventDefault();
sendMessage(); // 发送
}
}
// 手动实现判断是否为输入法合成状态
let isCompositionInputType = false;
const onInput = (event) => {
isCompositionInputType = ["insertCompositionText", "insertFromComposition"].includes(event.inputType);
}