スマホ対応のWebサイトやアプリで、タップに対してなにか動作させたい時、clickイベントを使うとすこし反応が鈍く感じることがあります。
スマホなどのタッチデバイスでは、clickイベントの発火にわずかなタイムラグが設けられているからのようです(多分ダブルタップを検出するためだと思います)。
そこで、touchstartやtouchendを組み合わせてタップ動作を検出する方法がよく取られますが、より汎用的にjQueryでclickなどと同じように扱えるtapイベントを作りました。
/* jQuery Tap Event */
(function($, window) {
"use strict";
var RANGE = 5,
events = ["click", "touchstart", "touchmove", "touchend"],
handlers = {
click: function(e) {
if(e.target === e.currentTarget)
e.preventDefault();
},
touchstart: function(e) {
this.jQueryTap.touched = true;
this.jQueryTap.startX = e.touches[0].pageX;
this.jQueryTap.startY = e.touches[0].pageY;
},
touchmove: function(e) {
if(!this.jQueryTap.touched) {
return;
}
if(Math.abs(e.touches[0].pageX - this.jQueryTap.startX) > RANGE ||
Math.abs(e.touches[0].pageY - this.jQueryTap.startY) > RANGE) {
this.jQueryTap.touched = false;
}
},
touchend: function(e) {
if(!this.jQueryTap.touched) {
return;
}
this.jQueryTap.touched = false;
$.event.dispatch.call(this, $.Event("tap", {
originalEvent: e,
target: e.target,
pageX: e.changedTouches[0].pageX,
pageY: e.changedTouches[0].pageY
}));
}
};
$.event.special.tap = "ontouchend" in window? {
setup: function() {
var thisObj = this;
if(!this.jQueryTap) {
Object.defineProperty(this, "jQueryTap", {value: {}});
}
$.each(events, function(i, ev) {
thisObj.addEventListener(ev, handlers[ev], false);
});
},
teardown: function() {
var thisObj = this;
$.each(events, function(i, ev) {
thisObj.removeEventListener(ev, handlers[ev], false);
});
}
}: {
bindType: "click",
delegateType: "click"
};
$.fn.tap = function(data, fn) {
return arguments.length > 0? this.on("tap", null, data, fn): this.trigger("tap");
};
})(jQuery, this);
これをtapイベントを使う前にあらかじめ書いておきます。
2016年3月5日追記:タッチデバイスで、clickイベント発生元とtapイベントをバインドした要素が同じ時のみ、クリックイベントのデフォルト動作をキャンセルするよう修正しました。
使い方
$(function() {
$("#btn").on("tap", function(e) {
/* なんらかの動作 */
});
/* ショートカット */
$("#btn").tap(function(e) {
/* なんらかの動作 */
});
});
こんな風にclickイベントなどと同じように使えます。unbindするときも.off("tap")
で可能です。
PCなど非タッチデバイスではtapという名のclickイベントとして動くので、別途click時の動作を書く必要はありません。
2016年3月5日追記:非タッチデバイスではclickイベントのデフォルト動作はそのままなので、キャンセルする場合はpreventDefault()
などでキャンセルしてください。タッチデバイスではclickイベントのデフォルト動作は自動でキャンセルされます。
簡単な解説
jQueryではjQuery.event.special
にプロパティを追加することでイベントを追加することができます。今回はjQuery.event.special.tap
としてtapイベントを定義しています。
setup
にbind時の処理、teardown
にunbind時の処理を書きます。
"ontouchend" in window
でタッチに対応しているか判別して、非タッチデバイスの場合は単純にclickイベントのエイリアスになるようにしています。
処理自体はtouchstartでフラグをオンにして、フラグオン時にtouchendが発火したらタップされたと判定しています。ただそれだけだと、スクロールしたい場合もタップされたと誤認識してしまうため、touchmoveである程度指が動いたらフラグをオフにするようにしています。
touchmoveが発火したら問答無用でフラグをオフにする方が簡単ですが、それだと判定がシビアになってしまい使いづらくなります。
余談
jQuery Mobileにはtapイベントが実装されているようです。
Comments