Предотвращение событий эмуляции мыши (т.е. щелчка) из событий касания в Mobile Safari/iPhone с использованием Javascript
Выполняя одностраничное Javascript-приложение с интерактивными элементами DOM, я обнаружил, что последовательность "mouseover-mousemove-mousedown-mouseup-click
" происходит в последовательности после последовательности событий < <21 > .
Я также обнаружил, что можно предотвратить события "mouse*-click
", выполнив "event.preventDefault()
" во время события touchstart
, но только тогда и не во время touchmove
и touchend
. Это странный дизайн, потому что, поскольку во время touchstart
невозможно узнать, есть ли у пользователя намерение перетаскивать или прокручивать или просто нажать/щелкнуть по элементу.
В итоге я установил флаг "ignore_next_click", привязанный к отметке времени, но это, очевидно, не очень чисто.
Кто-нибудь знает лучший способ сделать это, или мы что-то упускаем?
Обратите внимание, что хотя a "click" может быть распознан как "touchstart-touchend
" (т.е. нет "touchmove
" ), есть определенные вещи, такие как фокус ввода с клавиатуры, который может произойти только при правильном click
.
Ответы
Ответ 1
Просто предотвратить touchend
событие. Это позволит браузеру прокручивать страницу, когда вы касаетесь элемента, но не позволяет ему генерировать события искусственной мыши.
element.addEventListener('touchend', event => {
event.preventDefault();
});
Ответ 2
У меня возникли проблемы с использованием кросс-платформенных приложений HTML5/JS. Единственный реальный ответ для меня состоял в том, чтобы предотвратить Default на событиях касания и фактически управлять состояниями касания и стрелять, перетаскивать и т.д. События самостоятельно по моей логике. Это звучит намного сложнее, чем есть на самом деле, но перемешанные события click/mouse отлично работают на большинстве мобильных браузеров.
Нажмите и добавьте дополнительную последовательность мыши для удобства (и совместимости). Мое правило - если оно для вашего удобства, но оно неудобно, лучше всего его убить.
Что касается полей ввода, им нужны только события touchhend. Я убил события click/mouse и по-прежнему позволял мобильным браузерам правильно реагировать на входящие данные. Если он все еще дает вам проблемы, вы можете изменить обработчик событий только для того, чтобы подавлять события только на невводах:
function touchHandler(event) {
var shouldIgnore = event.target != null
&& ( event.target.tagName.toLowerCase() == "input" || event.target.tagName.toLowerCase() == "textarea" );
if(!shouldIgnore) e.preventDefault();
}
Ответ 3
Я сделал решение самостоятельно, так как я не нашел достаточного решения в другом месте:
var isTouch = ('ontouchstart' in window);
function kill(type){
window.document.body.addEventListener(type, function(e){
e.preventDefault();
e.stopPropagation();
return false;
}, true);
}
if( isTouch ){
kill('mousedown');
kill('mouseup');
kill('click');
kill('mousemove');
}
Проверка isTouch
позволяет вещам работать нормально на устройствах ввода-вывода, но убивает эмулированные события в Safari/iOS. Трюк заключается в использовании useCapture = true
в вызове addEventListener
, чтобы мы зачерпывали все события мыши на странице без взлома кода во всем веб-приложении. См. Документацию для функции здесь: https://developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener?redirectlocale=en-US&redirectslug=DOM%2Felement.addEventListener
Edit:
Теперь, когда библиотеки для решения этой проблемы лучше, вы можете просто использовать что-то вроде Fastclick в качестве альтернативы (https://github.com/ftlabs/fastclick).
Ответ 4
Если вам необходимо поддерживать устройства, которые поддерживают как мышь, так и сенсорный, другое решение - использовать прослушиватель событий захвата, который останавливает все события мыши, которые происходят либо
- в течение задержки после события касания
- в том же положении, что и событие касания
- в том же целевом элементе, что и событие касания
Информация (время, позиция или целевой элемент) события касания может быть записана в другом прослушителе событий записи.
Ответ 5
Обертывание кода только для мыши в функцию Window.matchesMedia - самый чистый способ, который я нашел.
if (window.matchMedia('(hover: hover), (any-hover: hover), (-moz-touch-enabled: 0)').matches) {
el.addEventListener('mouseover', ev => {
// mouse handler, no simulated hover
}
}
Это работает для предотвращения симулированных зависаний, но, вероятно, также предотвратит симулированные щелчки.
Примечание. Начиная с версии 58 в Firefox должна быть включена опция -moz-touch.
Ответ 6
Это решение позволяет вам прослушивать PointerEvents
если они существуют, затем TouchEvents
если они существуют, и MouseEvents
если ни один из двух других не существует. Mobile Safari по-прежнему будет вызывать как touchstart
и mousedown
, но вы будете слушать только touchstart
.
if (window.PointerEvent) { /* decent browsers */
etouch.addEventListener('pointerdown', (e) => {
console.log('pointerdown');
});
}
else if (window.TouchEvent) { /* mobile Safari */
etouch.addEventListener('touchstart', (e) => {
console.log('touchstart');
});
}
else { /* desktop Safari */
etouch.addEventListener('mousedown', (e) => {
console.log('mousedown');
});
}
Ответ 7
Создание быстрых кнопок для мобильных веб-приложений имеет свое решение проблемы.
Также следует помнить, что при использовании IE10 preventDefault() не останавливает события призрака/синтетического/эмулируемого мыши после события MSPointerDown, поэтому истинное кросс-браузерное решение сложнее.
Ответ 8
Вы можете попытаться выйти из функции при событиях click, mousedown или mouseup, когда устройство поддерживает сенсорные события.
use.addEventListener("click",function(e){
// EXIT FUNCTION IF DEVICE SUPPORTS TOUCH EVENTS
if ("ontouchstart" in document.documentElement) return false;
// YOURMOUSEONLY CODE HERE
});
Ответ 9
Использование 'pointerwhatever'
вместо 'mousewhatever'
, похоже, отлично работает в современных браузерах (2019).
то есть они изобрели способ иметь одинаковый код для всех устройств ввода.