写点什么

Web 键盘输入法应用开发指南 (8) —— 模拟事件

作者:天择
  • 2022 年 3 月 15 日
  • 本文字数:2708 字

    阅读完需:约 9 分钟

Web 键盘输入法应用开发指南 (8) —— 模拟事件

引言

在这篇文章中,我们来来聊聊如何在 Web 应用中模拟各类事件的触发。有时候我们需要通过程序脚本,主动触发一些内置事件(如键盘、鼠标、触碰等),或者自定义事件,以满足业务需求。另外,在做 Web 程序的自动化时,模拟事件的触发也是必备的技能。

isTrusted 属性

在讨论模拟事件之前,我们有必要理解事件对象上的一个属性isTrusted[1],它表示事件是否受信。一般来说,由用户操作产生的事件是受信的,该属性为true;而由程序脚本产生和触发的事件是不受信的,该属性为false


浏览器只会实现 isTrusted 值为 true 的事件的效果。比如,你用脚本生成了一个 keydown 事件,并通过EventTarget.dispatchEvent把它派发给某个输入框控件元素。此时,如果给改控件添加事件处理程序,keydown 事件的确会触发,不过isTrusted值一定为false,且输入框内不会产生任何内容。这里所谓事件的效果,就是 UI 控件上的变化(字符输入),以及后续事件


浏览器这样处理是出于安全性的考虑,试想如果通过脚本可以模拟真实输入事件,那么恶意脚本就可以修改页面的内容,模拟用户与页面的交互行为了,这是比较危险的。isTrusted 属性是只读的,通过脚本无法改变其值,因此是安全的。


我之前开发时遇到一个案例,关于 iOS 设备上的韩语输入法。韩语输入时,弹出的软键盘上会有一个输入的中间状态,直到用户按回车等键结束输入、触碰屏幕某个部分,或者直接关闭软键盘,这个输入状态才被清空。我遇到的问题是,程序的某个步骤阻止了 touch 事件的传播,导致韩语的中间状态不能被清空,从而引入了 bug。


我开始以为自己通过程序模拟一个 touch 事件继续传播,就会解决问题。理解了上面的内容就可以知道,iOS 不会处理模拟的 touch 事件,毕竟它修改了软键盘上输入法的状态,这是不安全的,而该事件的isTrusted自然是false的。


如果是这样的话,模拟事件是不是毫无意义呢?也不是。毕竟,你用脚本生成的事件对象,也可以指派给某个控件,并在适当时候触发,也可以通过事件处理器监听并采取相应行为。这可以帮助我们测试一些键盘或者输入法场景的业务逻辑,虽然并不能做 UI 上的改动。下面我们来看如何模拟事件触发。

模拟事件

由于我们这个系列以键盘和输入法应用为主题,这里就着重介绍键盘事件模拟,其他交互事件如鼠标、触屏可以参考公开的文档。定义一个键盘事件可以使用KeyboardEvent接口[2]:


let keyEvent = new KeyboardEvent("keydown", {    key: 'a',    code: 'KeyA',    location: 1,    shiftKey: true,    keyCode: 65,    bubbles: true,    cancelable: true});
element.dispatchEvent(keyEvent);
复制代码


可以直接给KeyboardEvent构造器传入事件的类型以及初始化的选项。有的程序会使用initKeyboardEvent方法来初始化事件对象,不过这样的写法已经被废弃[3]。最后调用dispatchEvent由相应的控件派发这个事件[4],就可以使用 event handler 处理这个事件了。


事件的初始化选项支持很多字段,比如keycodebubbles等,可以根据需要设置,具体支持列表可以参考文档[2]。你也可以在这里设置isTrusted属性,看看是什么效果。


同理,你也可以创建一个 Input 事件[5]:


let inputEvent = new InputEvent("input", {    bubbles: true,    composed: true,    data: "a",    inputType: "insertText",    isComposing: false});
复制代码


input 事件一个关键之处就是它的inputType属性,在之前的文章中也介绍过,不同的值意味着截然不同的 input 事件行为


模拟事件的一般实践是,先通过调试器获取正常输入时的事件序列,记录事件的顺序和属性值,然后依次模拟生成。你甚至可以写一个程序来录制这个输入过程,自动生成模拟事件的代码。

借助 Chrome 插件

你一定想问,有没有什么办法可以自动化地模拟事件并产生真实输入?据我目前的了解,方法有但是场景有限:那就是使用 Chrome 插件的调试器功能[6]。由于是在 Chrome 插件的环境中,且处于给开发者使用的 debug 模式,触发事件的真实效果可以认为是安全的。因此通过这种方法获得的模拟事件中,isTrusted属性值为true


首先要在manifest.json中开启 debugger:


..."permissions": ["storage", "activeTab", "scripting", "debugger"],...
复制代码


然后构造键盘事件。注意,Chrome 插件支持的键盘事件类型与浏览器原生的支持有些不同


let keyDownEvt = {    "type": "keyDown",     "key": "a",    "code": "KeyA",    "text": "a",    "windowsVirtualKeyCode": 65,    "nativeVirtualKeyCode": 65,    "macCharCode": 65}...
复制代码


这里的 keydown 事件的类型是keyDown而不是keydown,常见的类型列表可以在这里找到[7]。注意,这里事件的text属性也很关键,必须设置这个值才能触发浏览器中 keydown 后面的事件,比如 input 事件等。然后可以通过sendCommand方法将事件发送到页面,这个页面就是调试器所 attach 的页面


chrome.debugger.sendCommand({ tabId: tabs[0].id }, "Input.dispatchKeyEvent", keyDownEvt);
复制代码


这里的 command 类型为Input.dispatchKeyEvent,表示与 input 相关的用于分发 key 事件的命令,debugger 还支持多种其他类型的命令,比如使用Input.imeSetComposition命令设置当前 IME 的候选字词。详细的列表可以从这里找到[7]。上述方法返回的是一个 Promise,因此需要注意异步的处理,否则插件理会报错。


重新加载插件后,打开一个页面,将焦点放入输入框,然后通过插件发送命令,就可以得到如下事件:


模拟按键事件


同时在浏览器页面中,也可以看到实际的按键输出。我们注意到,这里的isTrusted属性值为true。通过这种方法,我们就可以在 Chrome 中实现按键的模拟和自动化输入了。

总结

在这篇文章中,我介绍一些模拟键盘事件的方法,以及一些跟安全性相关的限制。其中基于 Chrome 插件的方法可以实现接近现实的模拟,只不过它依赖于插件的实现,不能跨浏览器实现。有了这些方法,开发者可以对 Web 应用程序的输入功能进行自动化测试,或者通过已有的 case 套件,对新特性执行回归测试。下篇我们来看一些与键盘输入法相关的其他主题。

参考阅读

[1] isTrusted Property

[2] Keyboard Event

[3] initKeyboardEvent Method

[4] dispatchEvent Method

[5] Input Event

[6] Chrome Extension Debugger

[7] Key Event Types in Chrome Debugger

系列导航

如果您对这个系列感兴趣,可以通过下面的导航找到对应文章👇🏻。

Web 键盘输入法应用开发指南(1)— 基本概念

Web 键盘输入法应用开发指南(2)— 键盘事件

Web 键盘输入法应用开发指南(3)— 输入法事件

Web 键盘输入法应用开发指南(4)— 组合键

Web 键盘输入法应用开发指南(5)— 实战技巧

Web 键盘输入法应用开发指南(6)— 开发实战(一)

Web 键盘输入法应用开发指南(7)— 开发实战(二)

Web 键盘输入法应用开发指南(8)— 模拟事件

发布于: 刚刚阅读数: 4
用户头像

天择

关注

还未添加个人签名 2020.09.07 加入

爱看点书的软件工程师 主页:zesi.tech 欢迎交流~

评论

发布
暂无评论
Web 键盘输入法应用开发指南 (8) —— 模拟事件_JavaScript_天择_InfoQ写作平台