51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

奇了怪了,输入法和JS Enter回车提交冲突


键盘输入封面图

一、问题描述

最近遇到了个bug,使用 div 模拟输入框,需要加上Enter回车键快捷提交,于是,我就写了类似下面的代码:

<div id="input" placeholder="中文输入法开启时候回车" contenteditable="plaintext-only"></div>
input.addEventListener('keydown', function (event) {
    if (event.code == 'Enter') {
        event.preventDefault();
`    console.log('表单提交触发了');
}
`
});

结果发现,当打开中文输入法,希望回车确认英文的时候,会触发提交行为的执行。

如下实际渲染效果(iframe内嵌页面):

奇了怪了,我开发这么多年,之前似乎没遇到这个问题啊,怎么回事?

是浏览器升级了,是macOS系统原因,还是 div 输入框特殊行为?

后来整了个最原始的demo测试研究了下,找到了原因,原来是这么回事!

二、中文输入法与keyCode

以前,我处理键盘行为,都是用的 event.keyCode,无他,就是兼容性好。

后来看 MDN 文档,说 keyCode 要废弃了,不推荐使用,如下图所示:

keyCode废弃

于是就研究了下,现在都推荐使用 event.key 或者 event.code 来判断按键内容。

为此,我还专门写了篇文章"告别JS keyCode"。

于是,这次的项目,我就使用了 event.code == 'Enter' 来判断用户是否按下了回车键。

本身逻辑上没有任何问题,但是实际操作却不是这么回事,只要用户输入框处于聚焦状态,此时,你只要按下回车键,就会触发相关的行为,哪怕中文输入法开启。

这就导致了回车冲突的产生,但是,使用 event.keyCode 却没有此问题,因为keyCode有个我过去曾误认为异常的特性,那就是中文输入法开启的时候,你按下任意的键,返回的 keyCode 值都是229。

中文输入法均返回keyCode 229

以前以为是问题,现在才明白是特性。

not a bug feature

解决之道

知道了原因,问题就好解决了,一是使用传统的keyCode属性判断好了,什么废弃不废弃的,不用考虑那么多,浏览器不会那么傻,真的不支持这个属性值的,那不知道有多少Web应用要哭爹喊娘了。

input.addEventListener('keydown', function (event) {
    if (event.keyCode == 13) {
        event.preventDefault();
`    console.log('表单提交触发了');
}
`
});

此时,当中文输入法开启,输入内容再回车的时候,由于event.keyCode值是229,就不会执行表单提交,而是写入中文拼音,符合预期。

输入法关闭,此时回车的keyCode值是13,进入了预期的逻辑判断,执行表单提交行为,完美!

解决之道2

二是使用event.key代替event.code,如下所示:

input.addEventListener('keydown', function (event) {
    if (event.key == 'Enter') {
        event.preventDefault();
`    console.log('表单提交触发了');
}
`
});

因为中文输入法开启的时候,任何常规按键的event.key返回值都是 'Process',关闭的时候才会是 'Enter',也可以避免中文输入法的回车冲突问题。

三、compositionstart解决方法

如果有人非要说,我就是喜欢KeyboardEvent.code,非他不嫁,我同时还希望输入法不会出来搞事情,有没有可能既要还要呢?

还是可以的,可以使用compositionstart和compositionend时间进行处理。

当中文输入法开启的时候,会触发compositionstart事件,关闭的时候会触发compositionend事件,因此,我们可以增加一个标志量,当输入法启动时候,不再触发对应的行为。

JS代码示意如下:

var flag = true;
input.addEventListener('keydown', function (event) {
    if (flag && event.code == 'Enter') {
        event.preventDefault();
        console.log('表单提交触发了');
    }
});
input.addEventListener('compositionstart', function (event) {
    flag = false;
});
input.addEventListener('compositionend', function (event) {
    flag = true;
});

眼见为实,下面的输入框就是修复后的iframe内嵌页效果,可以看到,当输入法开启,你再回车,是不会触发提交行为的:

更新

根据评论反馈,直接使用 event.isComposing 判断即可,无需这么麻烦~

三、如果是input输入框的解决方法

如果输入框不是div模拟的输入框,而是原生的 input 输入框,还有一种比较好的解决方法,那就是走原生form表单提交,JS代码都不用写,浏览器自动识别回车,也没有各种乱七八糟的判断。

示意:

<form>
    <input required placeholder="中文输入法开启时候回车">
    <button type="submit">提交</button>
</form>
var form = document.querySelector('form');
var times = 0;
form.addEventListener('submit', function (event) {
    event.preventDefault();
`console.log('表单提交触发了' + (times ? ' +' + times: '~'));

times++; ` });


实际演示效果:

四、结束说点什么

bug的出现还是归结于自己对键盘事件行为的理解还不够深入,这就是要在一线干活的好处,遇到问题,你才能对技能的掌握更加深入彻底。

所以,如果你想成为写一辈子代码的人。

不要花太多精力在管理上,你不去写代码,只了解一些表面的东西,是写不了一辈子代码的。

毕竟知识的更新迭代速度还是很快的。

好,就这些,日常工作学习的一点记录,希望可以帮到遇到类似问题的同学。

感谢阅读,欢迎分享!

???

(本篇完)

赞(2)
未经允许不得转载:工具盒子 » 奇了怪了,输入法和JS Enter回车提交冲突