Ошибка 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.