如何在Chrome 41中以编程方式创建TouchEvent?

15

我正在尝试为单元测试创建一个触摸事件。阅读完https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent之后,我原以为我可以这样做:

document.createEvent('TouchEvent');

但是我收到了这个错误:

Uncaught DOMException: Failed to execute 'createEvent' on 'Document': The provided event type ('TouchEvent') is invalid.

我看到了Creating and firing touch events on a touch enabled browser?,其中也说明了createEvent()是可行的方法。

我还尝试通过构造函数创建事件,这对于MouseEvent和WheelEvent等事件有效:

new window.TouchEvent()

但是我在这里也遇到了一个错误:

Uncaught TypeError: 非法构造函数

我尝试在Firefox 36中运行,但根据http://caniuse.com/#search=touch,看到以下信息并不感到惊讶:

NotSupportedError: 操作不受支持

运行后

document.createEvent('TouchEvent')

在Firefox中甚至没有window.TouchEvent构造函数,这并不令人意外。

我做错了什么?有任何想法吗?

4个回答

21

我不知道它在其他浏览器中是否有效,但在Chrome中,您可以这样做:

/* eventType is 'touchstart', 'touchmove', 'touchend'... */
function sendTouchEvent(x, y, element, eventType) {
  const touchObj = new Touch({
    identifier: Date.now(),
    target: element,
    clientX: x,
    clientY: y,
    radiusX: 2.5,
    radiusY: 2.5,
    rotationAngle: 10,
    force: 0.5,
  });

  const touchEvent = new TouchEvent(eventType, {
    cancelable: true,
    bubbles: true,
    touches: [touchObj],
    targetTouches: [],
    changedTouches: [touchObj],
    shiftKey: true,
  });

  element.dispatchEvent(touchEvent);
}

const myElement = document.getElementById('foo')

sendTouchEvent(150, 150, myElement, 'touchstart');
sendTouchEvent(220, 200, myElement, 'touchmove');
sendTouchEvent(220, 200, myElement, 'touchend');

测试特定浏览器是否支持 TouchTouchEvent

if (typeof Touch !== 'undefined' &&
    typeof TouchEvent !== 'undefined' &&
    Touch.length === 1 &&
    TouchEvent.length === 1) {
       sendTouchEvent(200, 200, myElement, 'touchmove');
}

查看Touch和TouchEvent构造函数


谢谢。这是当前的正确答案。在Chrome 56中有效。 - ElDog
这看起来不错,但对我不起作用:我无法像你那样创建Touch对象(“提供的参数与调用目标的任何签名都不匹配”),因为es6是这样声明的:declare var Touch: { prototype: Touch; new(): Touch; };。你使用的是哪个版本? - Micha Schwab
1
@MichaSchwab 我刚在 Chromium 59 上测试了这个片段,它仍然有效。我记得在 TypeScript 中遇到过同样的问题(应该填写一个问题)。我声明了自己的接口来解决这个问题。你可以在这里这里找到它。 - fhdhsni
是的,我在使用TypeScript时也遇到了这个问题。这是一个好主意!我会尝试一下,谢谢! - Micha Schwab
这在我用作Android 4.4 WebViews代理的Chromium 45中无法工作。我从控制台得到与OP描述的相同的错误。Touch.length === TouchEvent.length === 0。 - orion elenzil

3
我猜想,不抛出异常的唯一方法是更明确地指定您希望创建的事件类型:
var evt = document.createEvent('UIEvent');

evt.initUIEvent('touchstart', true, true);

TouchEventUIEvent的一个子类。

更新

如上所述,在实际设备上(或模拟设备),可以使用标准的document.createEvent方法轻松创建TouchEvent。

因此,可能需要使用try/catch:

try {

  document.createEvent('TouchEvent');

} catch (e) {

  console.log('No touching!');

}

我曾考虑过这样做,但我希望依赖于对象本身的类型;我们正在进行一些事件合成,并且使用HammerJS,因此多个事件类别具有相同的类型,例如“touchstart”。我可以使用UIEvent来使我的测试通过,但它不会具有正确的测试精神。 :) - exavatar
evt.constructor == TouchEvent/touch/.test(evt.type) 之间真的有这么大的区别吗?JavaScript 实际上并不涉及经典继承,所以我认为你正在尝试把一个方形钉子塞进一个圆形洞里 - 即使它很有活力 :-) - shennan
没有太大的区别,除了正则表达式匹配事件类型无法匹配我正在尝试测试的代码;我可能需要更改它。 - exavatar

3
似乎是这样:
document.createEvent('TouchEvent'); 

如果您正在使用实际的移动设备或移动仿真,则可以正常工作。


(仅限Windows 8+)如果您没有实际设备,您可以创建一个调用InitializeTouchInjection的程序。它将创建Chrome能够识别的虚拟触摸设备,并允许您创建TouchEvent。 - Oleh Nechytailo

2
更新:这实际上不会导致 TouchEvent 的实例。很遗憾。
我是这样做的:
var type = 'start'; // or move, end
var event = document.createEvent('Event');
event.initEvent('touch' + type, true, true);     
event.constructor.name; // Event (not TouchEvent)

您还需要设置事件的触摸。这是另一个棘手的问题,因为一些浏览器支持document.createTouch和document.createTouchList方法,而一些浏览器则不支持。在不支持的浏览器中,您只需创建一个类似于“TouchEvent”的JS对象数组即可。示例如下:

var point = {x: 10, y: 10 };
event.touches = [{
    target: someElement,
    identifier: Date.now() + i,
    pageX: point.x,
    pageY: point.y,
    screenX: point.x,
    screenY: point.y,
    clientX: point.x,
    clientY: point.y
}]

看起来你和shennan的方法是一样的,我很感激你们的帮助。这只是一个解决方法,不完全是我想要的。但是,也许我只能用它了。 - exavatar
关于您的更新:是的,那就是问题所在。我想要一个TouchEvent实例,而不是依赖于事件类型列表。 - exavatar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接