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

这篇文章发布于 2023年02月8日,星期三,23:48,归类于 JS实例。 阅读 20801 次, 今日 18 次 15 条评论

 

键盘输入封面图

一、问题描述

最近遇到了个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的出现还是归结于自己对键盘事件行为的理解还不够深入,这就是要在一线干活的好处,遇到问题,你才能对技能的掌握更加深入彻底。

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

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

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

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

感谢阅读,欢迎分享!

???

(本篇完)

分享到:


发表评论(目前15 条评论)

  1. 张三说道:

    新bing聊天的输入框都有这个问题,输入法中按回车就直接提交了,体验很差。。。

  2. Joseph说道:

    mac自带的中文输入法,input输入框的keyup和keydown事件,输入拼音按enter,打印ev.keyCode是13,ev.code和ev.key都是’Enter’。
    这个怎么办

  3. 4Ark说道:

    其实不用这么麻烦,我们可以使用 isComposing 属性:https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/isComposing

  4. iou90说道:

    旭哥 抱歉实在找不到联系方式 微博评论貌似也不能包含链接了 有个事想分享 safari tp 163终于把这个问题修了: https://github.com/WebKit/WebKit/commit/46c1b789ed0879837b4cd085fbd48e0ad702366d, iOS 11就开始的 调用uikit一个跟截图有关的方法后webview 所有 css transition就全失灵iOS Chrome iOS 微信都很容易复现: https://bugs.chromium.org/p/chromium/issues/detail?id=899130, https://bugs.chromium.org/p/chromium/issues/detail?id=1231712

  5. zzzzz说道:

    我碰到类似的问题, 不过是监听 esc 键(产品需求), 是 contenteditable div + 内部 input 场景, 但是 esc 的按键一直监听不到, 按了之后都是取消 input 的聚焦

  6. zzh说道:

    textarea 回车只想换行,但是触发了表单提交怎么办