しるてく

技術的な話をします

一部のAndroid4.x端末でtouchstartでe.preventDefault()してもclickイベントが発火する

Galaxy Nexus(Android 4.2.2)とか他のAndroid4.1.2の端末で、e.preventDefault()しているにも関わらずclickイベントが呼ばれてしまうバグ?

背景

iScrollを使用しているサービスを一部のAndroid 4.2.2, 4.1.2端末で見ると、clickイベントが2回呼ばれてしまう。
どうも、iScroll内では、touchstartでe.preventDefault()して、touchendでclickイベントを発火しているのだが、デフォルトのclickと、iScrollのclickの二回呼ばれるらしい。

問題の端末 Galaxy Nexus(android4.2.2)
SO-01E(android4.1.2)
Nexus S(android 4.1.2)
対象のブラウザ WebView, 標準ブラウザ (Chromeは問題なし)
使用ライブラリ iScroll 4

SO-04E(android4.1.2)、SC-04E(android4.2.2)や(4.0|2)系端末だと問題は起きない。もちろんiPhoneも。

コード

iScrollでは、touchendで、ダブルタップ検出のために250ms待ってからclickイベント発火させるみたいなことしているので、擬似的なのを実装。

<div id="click">ここをクリックすると</div>
<div id="result"></div>
 
<script>
  (function() {
    $('#click').on('touchstart', function(e) {
      e.preventDefault();
    }).on('touchend', function() {
      $(this).trigger('click', ['clickされたで']);
    }).on('click', function(e, message) {
      message = message || 'こいつぁ。。。';
      $('#result').append('<div class="child">' + message  + '</div>');
    });
  })();

DEMO

スマホしか対応してないけど。


動作

PCとか普通のブラウザの場合

クリックすると「clickされたで」だけが表示。10回クリックすれば10個表示。

問題の端末

クリックすると「clickされたで」「こいつぁ。。。」の要素が表示される。

対策

どうしようかな…。

フラグ的な何かで管理するとか?
  (function() {
    $('#click').on('touchstart', function(e) {
      e.preventDefault();
      $.data(this, 'is_preventdefault', 1);
    }).on('touchend', function() {
      $(this).trigger('click', ['clickされたで']);
    }).on('click', function(e, message) {
      if ($.data(this, 'is_preventdefault') === 0) return;

      $.data(this, 'is_preventdefault', 0);
      message = message || 'こいつぁ。。。';
      $('#result').append('<div class="child">' + message  + '</div>');
    });
  })();
iScroll 5にするとか

iScroll5だと大丈夫なんだけど、コードが違いすぎて、何が原因でなおってるのか分からん…。
e.stopImmediatePropagation(), e.stopPropagation()あたりかなと思いつつ、上のコードに仕掛けてみたけどダメだった。

まとめ

e.preventDefault()をtouchstartで呼ぶと、clickイベントが発火しないって認識だったんだけど、違うのかな。他の端末では認識通りの挙動したけど、単純にブラウザによって違うだけ?それともバグ?
よく分からんが、こういうケースに出会いましたよってメモで。