事前知識: ブラウザ毎の差異
Android
2.x, 3.x
2.x, 3.x では伝統的に TouchEvent がまともに使えません。
経験上、一部端末でdocument.createEvent('TouchEvent')
するとNOT_SUPPORTED_ERR
を吐くなどの現象が見られ、seleniumのこのコードでも、MouseEvents
を利用して無理矢理回避しています。
4.x (Stock Browser & WebView)
4.x では、やっと TouchEvent が利用できるようになります。
が、 イベントの初期化方法が後述する iOS のソレと異なっています。同じくselenium上のコードではこのように回避しているのが見受けられます。
// Android's initTouchEvent method is not compliant with the W3C spec.
とありますが、そもそもこのinitTouchEvent
は執筆時点(2014/10/07)でまだ標準化されていません。W3C - TouchEvents #NOTE_3
(この辺り、詳しくは後述)
4.x (Chrome for Android & Chromium WebView)
document.createEvent('TouchEvent')
でタッチイベントを生成することができます。
が、 Stock Browserと同様、initTouchEvent
の引数がiOSと異なります。(後述)
コンストラクタとしてのTouchEvent
, Touch
, TouchList
が存在しますが、いずれもnewするとIllegal constructor
として怒られます。(どんな引数を渡しても駄目)
(多分)Opera Mobileも同一の挙動を示すと思われます。
iOS
バージョン毎にわける必要はありません。
非常に優秀で革新的な iOS では、iOS2.0 の頃からTouchEvent
を利用することができます。Safari Developer Library - TouchEvent Class Reference
Windows & Mac
Internet Exproler 11
TouchEvent は使えません。PointerEvent なら使えます。PointerEvent を使えば良いんじゃないでしょうか。
Chrome(+Opera), Firefox
通常は TouchEvent を使用できませんが、開発ツール上で TouchEvent のエミュレートをONにすることでdocument.createEvent('TouchEvent')
することができます。
コンストラクタとしてのTouchEvent
, Touch
, TouchList
が存在しますが、いずれもnewするとIllegal constructor
として怒られます。(どんな引数を渡しても駄目)
また、ChromeとFirefoxでinitTouchEvent
の引数の順番が異なります。(後述)
Safari
document.createEvent('TouchEvent')
, TouchEvent
, Touch
, TouchList
などのコンストラクタともにそのままでは使えません。
iOSシミュレータもしくはiOSの実機を接続して、その端末のSafariのWebインスペクタを開いた場合は全て利用することができます。(= PCサイトでは使えない)
TouchEventを生成する
先ほどさらっと書きましたが、タッチイベントを生成する際のinitTouchEvent
メソッドが標準化されていないため、 各ブラウザ毎に引数が違う という血も涙もない状況です。また2.x, 3.x系のAndroidもサポートするのであれば、MouseEventを代替として用いる必要もあります。
さらに、initTouchEvent
を実行する際にはTouchList
という配列 like なオブジェクトを渡す必要があります。これは、touches
, targetTouches
, changedTouches
という現在の指の情報として TouchEvent に付与されます。
これらを生成するには、document.createTouch
, document.createTouchList
のメソッド、もしくは Safari のみ Touch
, TouchList
コンストラクタで生成することができます。
が、 document.createTouch
, document.createTouchList
は現在の Document オブジェクトが DocumentTouch でない場合は生えないので、以下のような Polyfill で逃げるしか現状は打つ手がない気がしています。
//!Document.prototype.createTouch by w3c spec createTouch (https://developer.mozilla.org/ja/docs/Web/API/DocumentTouch.createTouch)
if (!Document.prototype.createTouch) {
Document.prototype.createTouch = function(view, target, identifier, pageX, pageY,
screenX, screenY, clientX, clientY,
radiusX, radiusY, rotationAngle, force) {
return {
clientX : clientX,
clientY : clientY,
force : force,
identifier : identifier,
pageX : pageX,
pageY : pageY,
radiusX : radiusX,
radiusY : radiusY,
rotationAngle : rotationAngle,
screenX : screenX,
screenY : screenY,
target : target,
};
};
}
//!Document.prototype.createTouchList by w3c spec createTouchList (https://developer.mozilla.org/ja/docs/Web/API/DocumentTouch.createTouch)
if (!Document.prototype.createTouchList) {
(function(global) {
function TouchList(touches) {
this.length = 0;
if (!touches) {
return this;
}
// list type argument
else if (touches.length) {
var touch;
for (var i = 0, iz = touches.length; i < iz; i++) {
touch = touches[i];
this[i] = touch;
}
this.length = iz;
}
else {
this[0] = touches;
this.length = 1;
}
}
TouchList.prototype = {
constructor: TouchList,
identifiedTouch: function(id) {
var that = this;
for (var key in that) if (!global.isNaN(global.parseInt(key)) && that.hasOwnProperty(key)) {
if (that[key].identifier == id) {
return that[key];
}
}
return undefined;
},
item: function(index) {
return this[index];
}
};
Document.prototype.createTouchList = function(touches) {
return new TouchList(touches);
};
})(window);
}
以下では、"tap"というイベントを生成する例を記します。
initTouchEvent
- Chrome, Opera 編
参考: Chromium Source
var touch = document.createTouch(/* 引数省略 */);
var touches = document.createTouchList(/* 引数省略 */);
var event = document.createEvent('TouchEvent');
event.initTouchEvent(
touches, // {TouchList} touches
touches, // {TouchList} targetTouches
touches, // {TouchList} changedTouches
'tap', // {String} type
document.defaultView, // {Window} view
0, // {Number} screenX
0, // {Number} screenY
0, // {Number} clientX
0, // {Number} clientY
false, // {Boolean} ctrlKey
false, // {Boolean} alrKey
false, // {Boolean} shiftKey
false // {Boolean} metaKey
);
initTouchEvent
- Safari 編
参考: Safari Developer Library - TouchEvent Class Reference
var touch = document.createTouch(/* 引数省略 */);
var touches = document.createTouchList(/* 引数省略 */);
var event = document.createEvent('TouchEvent');
event.initTouchEvent(
'tap', // {String} type
true, // {Boolean} canBubble
true, // {Boolean} cancelable
document.defaultView, // {Window} view
1, // {Number} detail
0, // {Number} screenX
0, // {Number} screenY
0, // {Number} clientX
0, // {Number} clientY
false, // {Boolean} ctrlKey
false, // {Boolean} altKey
false, // {Boolean} shiftKey
false, // {Boolean} metaKey
touches, // {TouchList} touches
touches, // {TouchList} targetTouches
touches, // {TouchList} changedTouches
0, // {Number} scale(0 - 1)
0 // {Number} rotation
);
initTouchEvent
- Firefox 編
参考: mozilla-central mozilla/dom/events/TouchEvent.h
var touch = document.createTouch(/* 引数省略 */);
var touches = document.createTouchList(/* 引数省略 */);
var event = document.createEvent('TouchEvent');
event.initTouchEvent(
'tap', // {String} type
true, // {Boolean} canBubble
true, // {Boolean} cancelable
document.defaultView, // {Window} view
1, // {Number} detail
false, // {Boolean} ctrlKey
false, // {Boolean} altKey
false, // {Boolean} shiftKey
false, // {Boolean} metaKey
touches, // {TouchList} touches
touches, // {TouchList} targetTouches
touches // {TouchList} changedTouches
);
現実的な話
例えばAndroid2.3〜, iOS6〜、かつPCで表示した場合も問題なくタッチイベントを生成する場合は
- Android2.x, 3.xだったら
-
document.createEvent('MouseEvent')
して、引数をMouseEventに合わせてinitMouseEvent
して、TouchEventっぽく見せかけて(touches, targetTouches, changedTouchesをプロパティに追加)発火
-
- Android4.x (Stock Browser & Chrome)
-
document.createEvent('TouchEvent')
して、引数をChrome用のものでinitTouchEvent
して発火
-
- iOS
-
document.createEvent('TouchEvent')
して、引数をSafari用のものでinitTouchEvent
して発火
-
- PC Chrome & Opera
- Polyfillで
createTouch
,createTouchList
を生やした後、Android4.xと同一の処理
- Polyfillで
- PC Safari
- Polyfillで
createTouch
,createTouchList
を生やした後、Android2.x, 3.xと同一の処理
- Polyfillで
- PC Firefox
- Polyfillで
createTouch
,createTouchList
を生やした後、引数をFirefox用のものでinitTouchEvent
して発火
- Polyfillで
- PC Internet Exproler
- Polyfillで
createTouch
,createTouchList
を生やした後、Android2.x, 3.xと同一の処理
- Polyfillで
というのが現実解でしょうか。
しかし、Chrome などで UserAgent を変更した場合には iOS のフリをした Chrome という状況になるのでかなり判定も難しくなるのでは無いかと思います。
答え
諦めてCustomEvent
使え
(Android2.xはCustomEvent
ないのでそれもUIEvent
などで代替する必要があります)