質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.36%

スマホのタッチイベントで2つ処理が実行されてしまう

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 8,560

k499778

score 542

現在HTML,Javascript(jQuery)を使ってスマホ用Webページを作っています。
そこでおかしな現象が起こってしまい対処したいのですが、そのように対処するのがいいか検討中です。

結論から言うと、
touchstartによるイベント処理時、タップしたボタンだけでなく、そのボタンを押したことにより表示されるモーダル内のリンクも処理が実行されてしまいます。そのリンクがタップした場所に表示される場合に限ってですが。つまりタップした場所にボタンとリンクが時間差でも表示される時そのどちらもが実行されてしまいます。そのような現象の対応方法はどのようにすべきでしょうか?

コードは以下のようになっています。

var _e = ("ontouchstart" in document) ? 'touchstart':'click';
$(window).on("load resize",function(){
  $(#a_btn).on({_e,function(){
     // モーダルを表示する処理。hiddenになっているモーダルエリアをshowする。
  })
})

結果的に次のようにすることで対処はできたのですが、いまいち綺麗な直し方とは言えないですし、動きがモッサリしてしまったり、端末によって時間のズレが生じそうだったりと懸念点は多いです。

var _e = ("ontouchend" in document) ? 'touchend':'click';
$(window).on("load resize",function(){

  $(#a_btn).on({_e,function(){
   setTimeout(function(){
     // モーダルを表示する処理。hiddenになっているモーダルエリアをshowする。
  },10);

   // settimeoutを書く前は以下のような記述を入れていましたが、起動したモーダルはクリックされました。
   // _e.preventDefault();
})

touchendに変更し、setTimeoutを使っています。

どうも2つ処理が実行される原因がtouchstartのイベントが維持時間がありそうなのです。
イベントが瞬間的には知らず、少し続いているような。そのためsetTimeoutで実行を遅らせることにより、2つの処理が発生するのを防ぎました。
touchendは手が離れた瞬間から計測しないと処理にばらつきが出るためです。

このような暫定的な対応は致しましたが、かなり懸念点が残ります。
このような経験がある方などアドバイス頂ける方がいらっしゃいましたらよろしくお願いいたします。

ちなみに処理の最後に
・preventDefault();
・return false;
を書いても問題は解決されませんでした。

よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+4

質問者様がおっしゃっている現象は確かに起こります。

touchstartイベントが発生するタイミングはGoogleのWeb Fundamentals サイト上でのタップ操作をサポートするによると初めて要素に指が触れたとき、またはユーザーがマウスでクリックをしたときに発生と記載してあります。

なので、ボタンをクリックしてmodalが出現するUIで、modal内に設定してあるtouchstartイベントや、リンクなどのDOMの位置がボタンと一緒になった場合、画面上ではボタンの位置をtouchしているので、イベントが発生してしまいます。

なのでこの場合は、ボタン要素にevent.preventDefault()してあげてください。

質問者様のコードをお借りするとしたら以下の通りになると思われます。

var _e = ("ontouchstart" in document) ? 'touchstart':'click';
$(window).on("load resize",function(){
  $(#a_btn).on({_e,function(event){ //(event)を追記
    event.preventDefault() //ココを追記
  })
})

僕のローカル環境で再現したコードも貼っておきます。
コード内容は、以下のとおりです。

  • ボタンをtouchstartしたらmodalが画面全体に出る
  • modal自体にtouchstartイベントにmodalを閉じるような内容を付与、

通常のtouchstartのみではボタンのtouchstartとmodalのtouchstartが同時に呼ばれてしまうので、modalが開けません(詳しく言うと、ボタンのtouchstartをcallした瞬間に、modalが出て、touchstartが反応してしまって閉じる状態になる)

<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
  #modal{
    display: none;
    position: absolute;
    top:0;
    right: 0;
    left: 0;
    bottom: 0;
    margin: auto;
    background: rgba(0,0,0,.6);
  }
  #modal.is-active{display: block;}
  </style>
</head>
<body>
  <button id="button">modal出現</button>
  <div id="modal"></div>

  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

  <script>
    $('#button').on('touchstart', function(e){
      e.preventDefault()
      $('#modal').addClass('is-active')
    })

    $('#modal').on('touchstart', function(){
      $('#modal').removeClass('is-active')
    }) 


    //# sourceURL=userscript.js
  </script>
</body>
</html>

繰り返しになりますが、ボタンのtouchstartイベントにevent.preventDefault()してあげてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/06 11:56

    追記致します。

    キャンセル

  • 2017/09/12 08:13

    大変遅くなってしまいましたが、追記しました。

    キャンセル

  • 2017/09/12 10:37

    すいません。
    2点ほど気になる点があります。

    1. _e.preventDefault()はエラーなく実行されますか?
    var _e = ("ontouchend" in document) ? 'touchend':'click';
    と書いてありますが、_eはただのstringではないでしょうか。

    preventDefault()メソッドがあるeventオブジェクトはcallback関数のargumentsに渡ってくるものです。

    $(#a_btn).on({_e,function(event){ //ここのeventです
    // _e.preventDefault() ではなく以下です。
    event.preventDefault()
    })

    2. preventDefault()は一番上に追記してください。
    $(#a_btn).on({_e,function(event){
    //ここです
    })

    ちょっと試してみてください。
    また、on()の中身は
    on({_e, function()})であっていますか?

    僕がその書き方を知らないので間違っていたら申し訳ないのですが
    .on('click', function(){ })じゃなくても動きましたでしょうか?

    キャンセル

+1

いまいちわからないのですが、「登録したイベントが複数回起こる」というのであれば $(window).on("load resize",function(){}); の中で  $(#a_btn).on(); をしているので、ロード/リサイズのたびにイベントを重複して登録しているからです。

また、モーダルの仕様がわからないので回答しずらいです。「モーダル」が前面に配置して表示を消しているだけの場合、ボタンがあればクリックできます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/03 22:00

    回答ありがとうございます。
    これに関してはイベントの重複登録というより、1度しか発火していないけれど、ボタンとモーダルのリンクが押されてしまうイメージです。

    モーダルの仕様としてはおっしゃる通り前面にあって表示を消しているだけですね。だからなのかなあ。
    ただsetTimeoutでスリープするとボタンしかクリックされないので、やはり消えているモーダルのリンクを押しているわけではないのかなとも思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.36%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る