乐趣区

用js代码触发dom事件的实现方案

背景

前端编写测试用例时,在测试界面上的一些效果时,通常都希望能够模拟一些用户操作,而模拟用户操作最主要的方式就是用代码触发指定事件。通常一些元素上会自带一些触发事件的方法,例如 click、focus 等,但是如果是其他的事件,例如 mousedown、mouseup、mouseenter,这些事件怎么模拟呢?

思路

其实思路很简单,web 标准中定义了一系列 API 接口,其中 dispatchEvent 允许我们向一个指定的事件目标派发一个事件,而且使用这个方法触发的事件是可以正常触发我们的标准事件处理规则的(包括事件捕获和可选的冒泡过程),那么这就非常强大了,我们可以基于此做很多事情了。

实现方法

大致流程相信大家都已经想到了,无外乎以下几步操作:

  1. 创建要触发的事件实例
  2. 获取要触发事件的元素对象
  3. 调用元素对象的 dispatchEvent 方法,参数是目标事件实例
  4. 特殊事件要分析一下模拟方式,巧妙的实现

首先,创建事件实例

web 标准提供了 MouseEvent 接口,专指用户与指针设备(如鼠标) 交互时发生的事件。使用此接口的常见事件包括:click、dblclick、mouseup、mousedown。
通过构造函数 MouseEvent(typeArg, mouseEventInit), 可以生成一个新的 MouseEvent 对象。该构造函数接受两个参数,第一个参数为 typeArg,用于指定事件的名称,一般都是一个字符串。第二个参数为 mouseEventInit,可以初始化 MouseEvent 的字典,也就是指定一些该事件的属性值,比如鼠标事件常见的 screenX、screenY、clientX、clientY 等属性,同时,由于 MouseEvent 是继承于 UIEvent,UIEvent 又继承于 Event,所以 mouseEventInit 可以包含 UIEventInit 和 EventInit 中的属性。
总结一行代码就是:

const mouseEvent = new MouseEvent(typeArg, mouseEventInit);

这里大家看自己实际需要,去指定哪些属性的哪些值即可。

这里再说一句,不仅是鼠标事件,还有很多其他事件,只要浏览器支持,都可以使用的。具体参考:https://developer.mozilla.org…
这里面列出的事件,理论上都可以自己模拟并触发。

其次,就是在指定元素上触发该事件

有了事件,那么就可以去调用对应元素上的 dispatchEvent 方法触发了。这里简单,也就是一行代码:

document.getElementById("id").dispatchEvent(mouseEvent);

最后,特殊事件或者场景,分解操作来模拟

有些特殊事件或者场景,其实可以分析一波细节,然后分解成若干个事件连续触发,即可巧妙的实现。
这里举个例子,点击事件,其实本质是:触发一次 mousedown,同时在足够短的时间内再触发一次 mouseup, 那么这样即可间接实现一次 click 事件。即:

// 一段足够短的时间内
mousedown+mouseup=click

这样,其实在模拟一些特殊操作时,我们也可以实现了。比如,模拟用户的鼠标拖拽多选操作,其本质就是:现在某个元素上触发 mousedown 事件,然后执行 mousemove 事件,然后拖拽到某一个元素上时,触发 mouseup 事件;即:

// 模拟用户拖拽鼠标
(开始元素)mousedown+(截止元素)mouseup= 一次鼠标拖拽操作

这里细节可以足够多,看你实际场景按需模拟即可,比如鼠标移动过程中,鼠标进入某个元素时,该元素还会触发 mouseenter 事件,离开元素还会触发 mouseleave 事件。那就是:

// 更精确的模拟用户拖拽鼠标
(开始元素)mousedown+(中间元素)mouseenter+(中间元素)mouseleave+(截止元素)mouseup= 一次鼠标拖拽操作

总之,细节可以足够多,但是够你模拟出本次操作的基本测试点即可,别忘了,我们的前提是模拟用户操作,进而执行测试用例。

再进一步,你完全可以封装一些常见的用户操作,然后将方法暴露出来,在测试用例中引入,实现高度复用。

总结

总结下来呢,其实就是采用 MouseEventdispatchEvent两个 web 标准提供的接口,来通过代码触发事件,进而模拟用户的操作,达到测试用例中,模拟用户行为的目的。希望对你有所帮助。

注意事项

这里有些限制条件,需要大家注意下:

  1. 本文所说的方法,主要是用于自动化测试场景中的,正常开发项目中,不建议大家用这种方式触发事件,这可能会导致你的代码难以理解,影响代码的可维护性。
  2. 本文中主要用到的这两大接口,是有兼容性限制的,IE 基本上是无法使用的,但是,一般来讲,使用这种方法的场景多数都是单元测试或者自动化测试的场景,这些场景下一般都会有模拟浏览器环境,而且用的最多的就是 chrome 的内核,所以基本上在测试场景下,这种方式是可以放心使用的。
退出移动版