JSでtouchstartとclickイベントを2回発生させないようにしつつ同時に指定する

スマホではタッチイベントを使うということをあまり意識せずに clickイベント ですべて書いてしまっている人も多いと思います。スマホではタッチイベントで反応を速くしたい場合、どのような方法があるのか紹介します。

タッチイベントが使えるか判定


var event = 'ontouchstart' in window ? 'touchstart' : 'click';

$('.button').on(event, function() {
  // 何らかの処理
});

今はさすがにこの手法を使っていることはないと思いますが、昔はこのように対応していたこともありました。タッチイベントに対応していれば touchstart を使い、そうでなければ click イベントを使うという方法です。

しかし、この手法だとタッチにもクリックにも対応したWindowsタブレットなどのPCだと、 タッチが優先 されてしまいマウス操作ができなくなってしまいます。

preventDefault()する


document.querySelector('.button').addEventListener('touchstart', function(event) {
  // touchstar以降のイベントを発生させないように
  event.preventDefault();

  // 何らかの処理
  foo();  
});

document.querySelector('.button').addEventListener('click', foo);

touchstartclick イベントそれぞれで foo() という関数を実行するとします。 touchstart のときに event.preventDefault()click イベントを発生させないようにしています。


$('.button').on('touchstart click', function(event) {
  // touchstar以降のイベントを発生させないように
  event.preventDefault();

  // 何らかの処理
  foo();
});

jQueryを使うとイベントをまとめて指定できるのでスッキリ書くことができます。

ただし、この方法は Android4.3以下 では正しく動作しないことがあるので注意が必要です。軽く検証したところ、Kindle Fire HDX 4.3Nexus 4.2 では clickイベントが無効化されず2回実行されてしまう 現象が起こります。モダンブラウザでは問題がないので、古いバージョンを対応することがないのであれば、この手法はシンプルで使いやすいと思います。

フラグで判定


var flag = false;

document.querySelector('.button').addEventListener('touchstart', function() {
  flag = true;
  
  // 何らかの処理
  foo();
});

document.querySelector('.button').addEventListener('click', function() {
  if (flag) {
    flag = false;
  } else {
    // 何らかの処理
    foo();
  }
});

touchstart イベントのときに flagtrue にし、 click イベントを発生させないようにしています。


var flag = false;

$('.button').on('touchstart click', function(event) {
  if (event.type === 'touchstart') {
    flag = true;
  }

  if (flag) {
    flag = false;
  } else {
    // 何らかの処理
    foo();
  }
});

これもjQueryでまとめるとスッキリ書くことができます。

clickイベントだけ定義する

ん? 何言ってるの? touchstart は? と思うかもしれません。そもそも、このようにタッチイベントが使えるときは touchstart、そうではないときは click イベントを使うようにしていたのは clickイベントだと反応が遅い ためでした。具体的には touchendclick イベント間で 300〜350ms 反応が遅延してしまうのです。


<meta name="viewport" content="width=device-width">

しかし、最近のブラウザではこの1行を head 内に記述していれば遅延が起こることはないようです。 この表 が参考になります。空欄のところが 遅延あり の端末を示しています。iOSは iOS9.3 で遅延が解消されたものの、 iOS10 ではまた遅延が復活しているようです。実際に検証してみたところ、確かに遅延はありますが、ほんのわずかでした。


html {
  -ms-touch-action: manipulation;
  touch-action: manipulation;
}

また、 Windowsタブレット などのその他のブラウザ向けにこの指定もしておくといいでしょう。

実際、ドロワーメニュープラグインの有名どころのソースコードを見てみたところ、click イベントだけでした。例えば、 drawer.jshiraku.js などがそうです。

それでも古いブラウザまで絶対に対応したい場合は、この記事で紹介した方法か、FastClick を使うといいと思います。