一、问题描述
最近遇到了个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 要废弃了,不推荐使用,如下图所示:
于是就研究了下,现在都推荐使用 event.key 或者 event.code 来判断按键内容。
为此,我还专门写了篇文章"告别JS keyCode"。
于是,这次的项目,我就使用了 event.code == 'Enter'
来判断用户是否按下了回车键。
本身逻辑上没有任何问题,但是实际操作却不是这么回事,只要用户输入框处于聚焦状态,此时,你只要按下回车键,就会触发相关的行为,哪怕中文输入法开启。
这就导致了回车冲突的产生,但是,使用 event.keyCode
却没有此问题,因为keyCode有个我过去曾误认为异常的特性,那就是中文输入法开启的时候,你按下任意的键,返回的 keyCode 值都是229。
以前以为是问题,现在才明白是特性。
解决之道
知道了原因,问题就好解决了,一是使用传统的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的出现还是归结于自己对键盘事件行为的理解还不够深入,这就是要在一线干活的好处,遇到问题,你才能对技能的掌握更加深入彻底。
所以,如果你想成为写一辈子代码的人。
不要花太多精力在管理上,你不去写代码,只了解一些表面的东西,是写不了一辈子代码的。
毕竟知识的更新迭代速度还是很快的。
好,就这些,日常工作学习的一点记录,希望可以帮到遇到类似问题的同学。
感谢阅读,欢迎分享!
???
(本篇完)