web-dev-qa-db-ja.com

JavaScriptタッチエンドとクリックのジレンマ

私はいくつかのJavaScript UIに取り組んでおり、タッチデバイスでの応答を改善するために「touchend」のような多くのタッチイベントを使用しています。しかし、私を悩ませているいくつかの論理的な問題があります...

多くの開発者が同じイベントで「タッチエンド」と「クリック」を組み合わせているのを見てきました。多くの場合、問題はありませんが、本質的にはタッチデバイスで関数が2回起動します。

button.on('click touchend', function(event) {
  // this fires twice on touch devices
});

たとえば、タッチ機能を検出し、イベントを適切に設定できることが提案されています。

var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click';
button.on(myEvent, function(event) {
  // this fires only once regardless of device
});

上記の問題は、タッチとマウスの両方をサポートするデバイスで破損することです。ユーザーが現在デュアル入力デバイスでマウスを使用している場合、「タッチエンド」のみがボタンに割り当てられるため、「クリック」は発生しません。

別の解決策は、デバイス(例:「iOS」)を検出し、それに基づいてイベントを割り当てることです。 iPadのタッチエンドで2回呼び出されるイベントをクリック 。もちろん、上記のリンクのソリューションはiOS専用であり(Androidまたはその他のデバイス)ではなく、かなり初歩的な問題を解決するための「ハック」のようです。

別の解決策は、マウスの動きを検出し、それをタッチ機能と組み合わせて、ユーザーがマウスまたはタッチを使用しているかどうかを判断することです。もちろん、検出したいときからユーザーがマウスを動かしていないかもしれないという問題は...

私が考えることができる最も信頼できる解決策は、単純な debounce 関数を使用して、関数が短い間隔(たとえば100ms)内に一度だけトリガーすることを確認することです:

button.on('click touchend', $.debounce(100, function(event) {
  // this fires only once on all devices
}));

何か不足していますか、それとももっと良い提案がありますか?

編集:投稿後にこのリンクを見つけましたが、これは上記と同様の解決策を示唆しています: 「タッチスタート」と「クリックをバインドする方法'イベントが両方に応答しない?

41
suncat100

1日の研究の後、最善の解決策はclickに固執し、 https://github.com/ftlabs/fastclick タッチ遅延を削除します。これがtouchendと同じくらい効率的であることは100%確信はありませんが、少なくともそう遠くはありません。

stopPropagationpreventDefaultを使用して、タッチでイベントを2回トリガーすることを無効にする方法を見つけましたが、適用される要素によっては他のタッチジェスチャに干渉する可能性があるため、これは危険です。

button.on('touchend click', function(event) {
  event.stopPropagation();
  event.preventDefault();
  // this fires once on all devices
});

実際、いくつかのUI要素でtouchstartを組み合わせるソリューションを探していましたが、click上記のソリューション以外。

30
suncat100

この質問は回答されていますが、更新する必要があるかもしれません。

Googleからの通知 によると、以下の行を<head>要素。

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

それでおしまい!そして、クリックイベントとタッチイベントの間に違いはなくなります!

6
quangpdt

はい、ダブルタップズームを無効にする(したがってクリック遅延)のが通常は最適なオプションです。そして、最終的にこれを行うための良いアドバイスがあります すべてのブラウザですぐに動作します

何らかの理由でそれをしたくない場合。 UIEvent.sourceCapabilities.firesTouchEvents 冗長なclickを明示的に無視します。 このためのポリフィル は、デバウンスコードに似た動作をします。

0
Rick Byers

debounce関数は、クリックの処理を100ミリ秒間遅らせます:

button.on('click touchend', $.debounce(100, function(event) {
  // this is delayed a minimum of 100 ms
}));

代わりに、すぐに起動するcancelDuplicates関数を作成しましたが、10ミリ秒以内の後続の呼び出しはキャンセルされます。

function cancelDuplicates(fn, threshhold, scope) {
    if (typeof threshhold !== 'number') threshhold = 10;
    var last = 0;

    return function () {
        var now = +new Date;

        if (now >= last + threshhold) {
            last = now;
            fn.apply(scope || this, arguments);
        }
    };
}

使用法:

button.on('click touchend', cancelDuplicates(function(event) {
  // This fires right away, and calls within 10 ms after are cancelled.
}));
0
Web_Designer

こんにちは、次の方法を実装できます。

function eventHandler(event, selector) {
    event.stopPropagation(); // Stop event bubbling.
    event.preventDefault(); // Prevent default behaviour
    if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks.
}

// Implement
$('.class').on('touchend click', function(event) {
    eventHandler(event, $(this)); // Handle the event.
    // Do somethings...
});
0
David