Ошибка JavaScript по сравнению с дилеммой щелчка

Я работаю над некоторым пользовательским интерфейсом javascript и использую множество сенсорных событий, таких как "touchhend" для улучшения ответа на сенсорные устройства. Тем не менее, есть некоторые логические проблемы, которые подталкивают меня...

Я видел, что многие разработчики смешивают "touchhend" и 'click' в одном и том же событии. Во многих случаях это не повредит, но по существу функция дважды срабатывает на сенсорных устройствах:

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
});

Проблема с вышеизложенным заключается в том, что он сломается на устройствах, поддерживающих как сенсорный, так и мыши. Если пользователь в данный момент использует мышь на устройстве с двумя входами, 'click' не будет срабатывать, потому что для кнопки назначается только "touchhend" .

Другим решением является обнаружение устройства (например, "iOS" ) и назначение события на основе этого: Нажмите событие, дважды вызываемое на iPad. Разумеется, решение в ссылке выше относится только к iOS (не к Android или другим устройствам) и кажется скорее "взломом" для решения чего-то довольно элементарного.

Еще одно решение - обнаружить движение мыши и объединить его с сенсорной способностью, чтобы выяснить, находится ли пользователь на мыши или прикосновением. Проблема, конечно, в том, что пользователь может не перемещать мышь, когда вы хотите ее обнаружить...

Самое надежное решение, о котором я могу думать, заключается в использовании простой функции debounce, чтобы просто убедиться, что функция запускается только один раз внутри короткий интервал (например, 100 мс):

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

Я что-то упускаю, или у кого-нибудь есть лучшие предложения?

Изменить: я нашел эту ссылку после моего сообщения, что предлагает аналогичное решение, как указано выше: Как привязать события "touchstart" и "click" , но не отвечать на оба?

Ответ 1

После дня исследования я решил, что лучшим решением является просто нажать и использовать https://github.com/ftlabs/fastclick, чтобы удалить задержку касания, Я не уверен на 100%, что это так же эффективно, как и коснуться, но, по крайней мере, далеко не так.

Я выяснил способ отключить триггерные события дважды при касании с помощью stopPropagation и preventDefault, но это изворотливо, поскольку это может помешать другим касаниям касания в зависимости от элемента, где он применяется:

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

Я действительно искал решение объединить touchstart на некоторых элементах пользовательского интерфейса, но я не вижу, как это можно комбинировать с кликом, отличным от решения выше.

Ответ 2

Этот вопрос отвечает, но, возможно, нуждается в обновлении.

В соответствии с уведомление от Google, не будет никакой задержки 300-350мс, если мы включим строку ниже в <head>.

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

Что это! И не будет никакой разницы между кликом и событием touch больше!

Ответ 3

Здравствуйте, вы можете реализовать следующий способ.

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...
});

Ответ 4

Ваша функция 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.
}));

Ответ 5

Да, отключение двойного нажатия (и, следовательно, задержка нажатия), как правило, является лучшим вариантом. И у нас наконец есть хороший совет для этого, который скоро будет работать во всех браузерах.

Если по какой-то причине вы не хотите этого делать. Вы также можете использовать UIEvent.sourceCapabilities.firesTouchEvents, чтобы явно игнорировать избыточный click. polyfill для этого делает что-то похожее на ваш код debouncing.