デスクトップ向けのブラウザーとスマートフォン向けのブラウザーどちらにも対応する場合、クリックとタップの両方のイベントリスナーをセットすることが良くあります。もちろん、デスクトップであればクリックのイベントリスナーのみを起動し、スマートフォンであれば タップのイベントリスナーのみを起動したいはずです。
しかし、スマートフォンではクリックとタップの両方のイベントリスナーが呼び出されてしまい困る場合があります。今回は、スマートフォンでタップのイベントリスナーだけを起動する方法について解説します。
イベントの発出の順番
マウスのクリックとスマートフォンのタップに関連するイベントには、click
, mouseup
, mousedown
, touchstart
, touchend
イベントがあります。例えば、これらすべてのイベントに対してイベントリスナーをセットしたとしましょう。ここでは、body 要素に対して各種イベントリスナーをセットし、pre 要素の中に発生したイベントの名前を追記します。
<pre id="console"></pre>
document.body.addEventListener('click', showEvent);
document.body.addEventListener('mousedown', showEvent);
document.body.addEventListener('mouseup', showEvent);
document.body.addEventListener('touchstart', showEvent);
document.body.addEventListener('touchend', showEvent);
const console_el = document.getElementById('console');
function showEvent(event) {
let text_node = document.createTextNode(event.type + '\n');
console_el.appendChild(text_node);
}
デスクトップ向けのブラウザーでマウスをクリックすると、次の順番でイベントが発生します。
mousedown
mouseup
click
タップ関連のイベントは発生しません。一方、スマトフォン向けブラウザーで画面をタッチすると、次の順番でイベントが発生します。
touchstart
touchend
mousedown
mouseup
click
スマートフォンでは、マウス関連のイベントも発生してしまいます。これが困るわけです。
event.preventDefault() メソッドが解決
前述の問題は、Event
オブジェクトの preventDefault()
メソッドで解決できます。イベントリスナー関数を次のように変更します。
function showEvent(event) {
event.preventDefault();
let text_node = document.createTextNode(event.type + '\n');
console_el.appendChild(text_node);
}
こうすることで、発生するイベントとその順番は次のようになります。
touchstart
touchend
preventDefault()
メソッドは、イベントが発生したときの規定の挙動(デフォルトアクション)を止めます。タップなどのタッチ関連のイベントの仕様は、W3C の Touch Events という仕様書に規定されています。そして、touchend
のデフォルトアクションとして mousedown
, mouseup
, click
イベントが挙げられています。つまり、touchend イベントが発生したら、mousedown
, mouseup
, click
イベントを発出することになっています。
クリック系イベントだけではダメなのか?
スマートフォンではタップしたらクリック系のイベントも発生して困るなら、touchstart
や touchend
を使わずにクリック系のイベントのみを使えばいいじゃないか、と思われるのではないでしょうか。もちろん、それで困らないならそれで良いですし、実際に筆者も click
イベントだけで何とかすることのほうが多いと言えます。
では、タッチ系イベントを使わないといけない場合とは、どんなときでしょうか。考えられる理由としては 2 つあります。
- タッチ系イベントで得られる情報が必要な場合
- パフォーマンスにシビアな場合
では、それぞれを詳しく見ていきましょう。
タッチ系イベントから得られる情報
touchstart
や touchend
イベントから得られる情報には、マウス関連のイベントから得られないタッチ特有の情報もあります。もしタッチ特有の情報が必要な場合は、touchstart
や touchend
イベントを使わざるを得ません。
タッチ関連の情報とは具体的には、他のタッチの情報です。近年のスマートフォンは、タッチは一点ではなく複数のタッチを同時に認識できます。touchstart
や touchend
イベントからは、それら他の指が触れている情報も得られます。詳細は MDN の TouchEvent の説明を読むと良いでしょう。
パフォーマンス向上
スマートフォンでは、タップすると、まずタッチ系のイベントが発生してからマウス系のイベントが発生します。もし、できる限りパフォーマンス良く反応したいなら、タッチ系のイベントを使ったほうが良いでしょう。
では、タッチ系のイベントとマウス系のイベントの発生タイミングの差どれくらいなのでしょうか。ほとんど差はないだろうと思うかもしれませんが、いちおう次のコードで確かめてみました。このコードでは、touchstart
から click
イベントが発生するまでの差を計測しています。
document.body.addEventListener('touchstart', showEvent);
document.body.addEventListener('click', showEvent);
let start = 0;
function showEvent(event) {
if (event.type === 'touchstart') {
start = Date.now();
} else if (event.type === 'click') {
window.alert((Date.now() - start) + ' ミリ秒');
}
}
ちょっと古い端末で申し訳ないのですが、iPhone 11 Pro の Safari で 100 ミリ秒前後の差がありました。Google Pixel 4 の Chrome では 50 ミリ秒前後の差でした。意外に Safari のほうが差が大きいです。
この 100 ミリ秒の差をどう考えるかにもよりますが、利用シーンによっては、その違いを感じる場合もあるでしょう。
まとめ
正直なところ、筆者はほとんどのシーンで click
イベントだけで何とかなっており、あまりタッチ系のイベントを扱うことがありません。ところが、稀にタッチ系のイベントを駆使しなければならないときがあり、いつも touchend
イベントのデフォルトアクションのことを忘れてしまいます。
今回の記事はその忘備録ですが、みなさんにもお役に立てば幸いです。 今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。