##一.click と 300ms 遅延
モバイルブラウザは特殊な機能を提供します:ダブルタップ(double tap)拡大
300ms の遅延はここから来ており、ユーザーがページに触れた後、ダブルタップ(double tap)動作かどうかを判断するためにしばらく待つ必要があり、すぐにシングルクリック(click)に响应するわけではありません。待つ時間は約 300ms です。以前に簡単な紹介がありました:[黯羽軽揚:HTML5 タッチイベント](/articles/html5 タッチイベント/)
モバイルイベントは touchstart、touchmove、touchend を提供していますが tap サポートは提供していません。主流のフレームワーク(ライブラリ)はすべて手動でカスタム tap イベントを実装し、300ms 遅延を消除し、ページ响应速度を向上させることを求めています。シンプルなページの場合、touchstart または touchend を tap として使用できますが、いくつかの問題があります。例えば、指が目標要素に接触し、押したままゆっくり响应領域から移出すると、touchstart イベントがトリガーされて対応するイベントハンドラが実行されます(本来トリガーされるべきではありません)。touchend イベントにも類似の問題があります。
さらに、ネイティブ touch イベントを使用してもクリック透過問題が存在します。click は touch シリーズイベント発生後約 300ms して初めてトリガーされるため、touch と click を混用すれば確実に点透問題を引き起こします。以下で詳細に説明します
##二.クリック透過問題
クリック透過現象には 3 種類あります:
- クリック透過問題:マスク層(mask)上の閉じるボタンをクリックし、マスク層が消失した後にボタン下面の要素の click イベントがトリガーされたこと���発見
マスク層の閉じるボタンに绑定されているのは touch イベントで、ボタン下面の要素に绑定されているのは click イベントです。touch イベントがトリガーされた後、マスク層が消失し、300ms 後にこの点の click イベントが fire され、event の target は当然ボタン下面の要素になります。ボタンがマスク層と一緒に消失したからです
- クロスページクリック透過問題:ボタン下面がちょうど href 属性のある a タグの場合、ページがジャンプします
a タグジャンプはデフォルトで click イベントがトリガーされるため、原理は上記と完全に同じです
- もう 1 つのクロスページクリック透過問題:今回は mask がなく、直接ページ内ボタンをクリックして新ページにジャンプし、その後新ページ中の対応位置要素の click イベントがトリガーされたことを発見
マスク層の道理と同じで、js 制御ページジャンプのロジックが touch イベントに绑定されており、かつ新ページ中の対応位置の要素が click イベントに绑定されており、かつページが 300ms 以内にジャンプを完了した場合、3 つの条件が同時に満たされると、この状況が発生します
あえて細分すれば 4 番目もありますが、確率は非常に低く、新ページ中の対応位置要素がちょうど a タグで、その後連続ジャンプが発生します……このようなものはすべてクリック透過問題です
##三.解決方案
問題は已经很明了で、多くの解決方案がありますが、思路は不外乎 2 種類です:
- touch と click を混用しない
touch の後 300ms に click がトリガーされる既然、touch のみを使用するか click のみを使用すれば自然に問題は存在しません
- touch の後の click を食べる(または消費する)
やはり tap を使用しますが、クリック透過が発生する可能性のある状況で追加の処理を行い、何かで遮る、または tap 後 350 ミリ秒遅延して mask を隠す、pointer-events、下面要素のイベントハンドラ内で検出を行う(グローバル flag と連携)など、食べられればよいです
詳細な解決方案:
- touch のみを使用
最もシンプルな解決方案で、クリック透過問題を完璧に解決
ページ内のすべての click を touch イベント(touchstart、'touchend'、'tap')に置き換えます。特に注意が必要なのは a タグで、a タグの href も click なので、削除して js 制御のジャンプに置き換えるか、直接 span + tap 制御ジャンプに変更します。要求が高くない場合、滑走または滑入でイベントがトリガーされることを気にしないなら、span + touchend で十分です。tap はサードパーティライブラリを導入する必要があるからです
a タグを使用しなくても問題ありません。モバイル app 開発では SEO を考慮する必要はなく、a タグを使用しても、一般にすべてのデフォルトスタイルを削除するので、直接使用 span の方がましです
- click のみを使用
下下策です。300ms 遅延をもたらすため、ページ内のあらゆるカスタムインタラクションに 300 ミリ秒の遅延が追加されます。考えるだけでも遅いです
touch を使用しなければ touch の後 300ms に click がトリガーされる問題は存在しません。インタラクション性の要求が高くない場合はこのようにできますが、強く非推奨です。速い方が良いに決まっています
- 何かで遮る
比較的愚かな方法で、絶対に使用しないでください
葉小釵の「菊花」大法。詳細情報は 【モバイル端互換問題研究】javascript イベントメカニズム詳解(モバイル互換を涉及) を参照
- tap 後 350ms 遅延して mask を隠す
改動が最小で、欠点は mask を隠すのが遅くなることです。350ms はstill 遅いと感じられます
mask に対してのみ処理を行えばよく、改動が非常に小さいです。要求が高くない場合、これを使用すると省力です
- pointer-events
比較的麻烦で欠陥があり、使用を推奨しません
mask 隠蔽後、ボタン下面の要素に pointer-events: none; スタイルを追加し、click を透過させ、350ms 後にこのスタイルを削除して响应を回復します
欠点は mask 消失後の 350ms 内、ユーザーはボタン下面の要素をクリックしても反応しないのを見ることができます。ユーザーの手速が非常に速い場合、必ず発見します
- 下面要素のイベントハンドラ内で検出を行う(グローバル flag と連携)
比較的麻烦で、使用を推奨しません
グローバル flag でボタンクリックの位置(座標点)を記録し、下面要素のイベントハンドラ内で event の座標点を判断し、同じであればあの憎らしい click なので、响应を拒否します
上記は単なる想法で、テストしていません。实在不行ならタイムスタンプを記録して判断し、350ms 待機します。こうすれば pointer-events とほぼ同じになります
- fastclick
使いやすい解決方案で、数 KB 余分にロードすることを気にしないなら、使用を推奨しません。バグに遭遇した人がいるからです。詳細情報は Fastclick 导致 click イベントが 2 回トリガーされる問題 を参照
まず fastclick ライブラリを導入し、その後ページ内のすべての touch イベントを click に置き換えます。実際には少し麻烦です。この数 KB を導入して点透問題を解決する価値はないと建議します。第 1 種の方法を使用する方がましです
##四.DEMO
多くのテストページを書きました。参照してください:Git @OSC ayqy / my.js
###参考資料
- 先輩博文若干
コメントはまだありません