14

I am trying to create a touch event for a unit test. After reading https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent, I expected that I would be able to do:

document.createEvent('TouchEvent');

But I get this error:

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

I saw Creating and firing touch events on a touch enabled browser?, which also seems to indicate that createEvent() is the way to go.

I also tried creating the event via constructor, which works for, say, MouseEvent and WheelEvent:

new window.TouchEvent()

But I get an error here, too:

Uncaught TypeError: Illegal constructor

I tried in Firefox 36, but based on http://caniuse.com/#search=touch, I wasn't surprised to see:

NotSupportedError: Operation is not supported

After running

document.createEvent('TouchEvent')

There is not event a window.TouchEvent constructor in Firefox, which is, again, not surprising.

Any ideas what I am doing wrong?

Community
  • 1
  • 1
exavatar
  • 259
  • 1
  • 3
  • 10

4 Answers4

20

I don't know if it works in other browsers but in chrome you can do something like

/* 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');

To test if a given browser supports Touch and TouchEvent

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

see Touch and TouchEvent constructors

fhdhsni
  • 1,529
  • 1
  • 13
  • 21
  • Thanks. This is the current correct answer. Works in Chrome 56. – ElDog Mar 10 '17 at 15:19
  • This looks good, but it doesn't work for me: I can't create the Touch object the way you do (`Supplied parameters do not match any signature of call target`) because es6 has it declared this way: `declare var Touch: { prototype: Touch; new(): Touch; };`. What version do you use? – Micha Schwab Aug 10 '17 at 19:42
  • 1
    @MichaSchwab I just tested the snippet on chromium 59, and it still works. I remember having the same problem in typescript (should've filled an issue). I declared my own interface to get away with that. You can find it [here](https://github.com/fhdhsni/weewikipaint/blob/master/src/scripts/globals.d.ts#L24-L32) and [here](https://github.com/fhdhsni/weewikipaint/blob/master/test/client/sendEvent.ts) – fhdhsni Aug 12 '17 at 07:52
  • Yeah I'm having this issue in Typescript as well. That's a great idea! I will try that, thank you! – Micha Schwab Aug 12 '17 at 16:32
  • this does not work in Chromium 45, which i'm using as a proxy for Android 4.4 WebViews. I get the same errors as described by the OP. Touch.length === TouchEvent.length === 0, from the console. – orion elenzil Aug 23 '19 at 21:46
3

I'm guessing the only way to do it without throwing an exception is to be more explicit in the type of event you wish to create:

var evt = document.createEvent('UIEvent');

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

The TouchEvent is a subclass of UIEvent.

Update

As mentioned above, while on an actual device (or emulating a device), one can easily create a TouchEvent using the standard document.createEvent method.

So perhaps a try/catch would be in order:

try {

  document.createEvent('TouchEvent');

} catch (e) {

  console.log('No touching!');

}
shennan
  • 10,798
  • 5
  • 44
  • 79
  • I was thinking of doing that, but I was hoping to depend on the type of the object itself; We're doing some event synthesis, and also using HammerJS, so multiple classes of event have have the same type, like 'touchstart.' I could certainly use UIEvent to make my test pass, but it wouldn't have the correct spirit of the test. :) – exavatar Mar 12 '15 at 19:38
  • Is there really such a difference between `evt.constructor == TouchEvent` and `/touch/.test(evt.type)`? JavaScript doesn't really deal in classical inheritance, so IMO you're trying to fit a square peg in a round hole - even if it is spirited :-) – shennan Mar 12 '15 at 20:00
  • No, not really much difference, other than regexing the event.type will not match the code I am trying to test; I might have to change that. – exavatar Mar 12 '15 at 21:58
3

It looks like

document.createEvent('TouchEvent'); 

works if you are using an actual mobile device or mobile emulation.

exavatar
  • 259
  • 1
  • 3
  • 10
  • (Windows 8+ only) If you don't have actual device you can create a program that calls [InitializeTouchInjection](https://msdn.microsoft.com/en-us/library/windows/desktop/hh802880(v=vs.85).aspx). It will create virtual touch device that Chrome recognizes and allows you to create TouchEvents. – Oleh Nechytailo Dec 23 '15 at 16:32
2

Update: This will not actually result in an instance of TouchEvent. lame.

I did it like this:

var type = 'start'; // or move, end
var event = document.createEvent('Event');
event.initEvent('touch' + type, true, true);     
event.constructor.name; // Event (not TouchEvent)

You'll also need to set the touches on the event. That's another can of worms as some browsers support the document.createTouch and document.createTouchList methods and some don't. In browsers that don't you just create and array of JS objects that are "TouchEvent-like". This looks like:

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
}]
Rob
  • 2,759
  • 1
  • 16
  • 11
  • It looks like both you and shennan have the same approach, and I appreciate the help. It is a workaround, and not exactly what I am working for. But, I might be stuck with it. – exavatar Mar 12 '15 at 21:34
  • Regarding your update: Yeah, that's the problem. I want an instance of TouchEvent, not to rely on a list of event.type – exavatar Mar 12 '15 at 21:57