2010-05-23 9 views
34

Al hacer una aplicación de una sola página Javascript con elementos DOM interactivos, he encontrado que la secuencia "mouseover-mousemove-mousedown-mouseup-click" ocurre en un grupo después de la secuencia de eventos "touchstart-touchmove-touchend".Prevenir eventos de emulación de mouse (es decir, hacer clic) desde eventos táctiles en Mobile Safari/iPhone usando Javascript

También he encontrado que es posible evitar que los "eventos" mouse*-click suceda haciendo una "event.preventDefault()" durante el evento touchstart, pero solamente entonces, y no durante el touchmove y touchend. Este es un diseño extraño, porque no es posible saber durante el touchstart si el usuario tiene la intención de arrastrar o deslizar o simplemente tocar/hacer clic en el elemento.

Terminé configurando una bandera "ignore_next_click" en algún lugar vinculada a una marca de tiempo, pero esto obviamente no es muy limpio.

¿Alguien sabe de una mejor manera de hacerlo, o nos falta algo?

Tenga en cuenta que mientras que un "clic" puede ser reconocido como una secuencia "touchstart-touchend" (es decir, sin "touchmove"), hay ciertas cosas, como foco de entrada de teclado, que sólo pueden ocurrir cuando se click evento adecuado.

+0

Estoy interesado en los eventos táctiles del iPad Safari también, pero no me queda claro cuál es el problema específico que se está tratando de resolver. Si todavía está trabajando en este tema, o lo ha resuelto, ¿quiere elaborarlo? – Tim

+0

Quiero ser capaz de manejar ciertos eventos como arrastrar y soltar, y también ser capaz de manejar eventos de "clic". Tengo que manejar los eventos de "hacer clic" como eventos correctos de "clic" (en lugar de tocar/activar) porque ciertas cosas, como el enfoque de entrada de teclado, solo * pueden * activarse dentro de un controlador de evento de clic. –

+0

Este problema es extremadamente molesto y afecta a Android también. –

Respuesta

4

Me he encontrado con problemas similares al hacer aplicaciones multiplataforma HTML5/JS. La única respuesta real para mí fue preventDefault en los eventos táctiles, y en realidad administro los estados táctiles y disparar eventos de clic, arrastra, etc. de acuerdo con mi lógica. Esto suena mucho más desalentador de lo que realmente es, pero los eventos de clic/mouse imitados funcionan perfectamente en la mayoría de los navegadores móviles.

Haga clic y el extra de ratón secuencia son todo lo que hay para su conveniencia (y la compatibilidad). Mi regla general: si es por su conveniencia, pero es inconveniente, es mejor que lo mate.

En cuanto a los cuadros de entrada, solo necesitan los eventos touchend. He eliminado los eventos de clic/mouse y todavía he podido permitir que los navegadores móviles respondan correctamente a los toques en las entradas. Si todavía le está dando problemas, puede modificar el controlador de eventos a sólo eventos Supress en los no entradas:

function touchHandler(event) { 
    var shouldIgnore = event.target != null 
      && (event.target.tagName.toLowerCase() == "input" || event.target.tagName.toLowerCase() == "textarea"); 

    if(!shouldIgnore) e.preventDefault(); 
} 
+0

Estoy probando esta solución tanto en iOS5 como en Android 2.2.1, y el problema es que si impido Default en 'touchstart', el' click' no se dispara, pero no puedo desplazar la página. Si impido Default en 'touchend', funciona de maravilla en iOS (solo si el toque es corto) pero dispara el 'clic' en Android. ¿Has notado este mismo problema? Pruébelo aquí: http://jsfiddle.net/3TBVc/ –

+0

Sí, me he encontrado con este problema (se manifiesta de varias maneras). Parece ser uno de los dolores más grandes en iOS <-> Android multiplataforma. Requiere algunos ajustes, pero al cancelar las cosas correctas, generalmente puede obtener el resultado deseado. http://jsfiddle.net/3TBVc/9/ Parece que funciona bien para mí en Android. Los campos de entrada pueden comer algunos eventos 'arrastrar' (en Android), pero es funcional. –

+0

Todavía está activando eventos de clic en mi dispositivo Android (y también en el iPhone para el primer botón). –

2

he hecho una solución de mí mismo, ya que no he encontrado una solución suficiente en otra parte:

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

La comprobación de isTouch permite que las cosas funcionen normalmente en los dispositivos de entrada de ratón, pero elimina los eventos emulados en Safari/iOS. El truco está en usar useCapture = true en la llamada al addEventListener para que recojamos todos los eventos del mouse en la página sin hackear el código en toda la aplicación web. Consulte la documentación para la función aquí: https://developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener?redirectlocale=en-US&redirectslug=DOM%2Felement.addEventListener

Editar:

Ahora que las bibliotecas para el manejo de este problema son mejores, sólo se puede usar algo como Fastclick como alternativa (https://github.com/ftlabs/fastclick).

+4

Esta es una buena solución cuando sabe que los dispositivos son de entrada táctil o de mouse. Pero cuando tiene algo así como una computadora portátil/tableta con Windows 8, tendrá que hacer otra cosa. – psayre23

+0

Nuestra solución a esto fue escuchar eventos de clic y de inicio táctil/detener/mover y solo registrar el evento de clic si 'ontouchstart' no estaba disponible. Safari móvil registra eventos de pulsación y clic, pero este método solo registra el evento táctil en ese caso. Además, estábamos bien sin tener en cuenta un evento mousemove. –

+1

votaron en contra porque la pregunta no era deshabilitar completamente los eventos del mouse si los eventos táctiles están disponibles ... – iRaS

0

Creating Fast Buttons for Mobile Web Applications tiene su solución al problema.

También tenga en cuenta que al usar IE10, prevenirDefault() no detiene los eventos del mouse fantasma/sintético/emulado después de un evento MSPointerDown, por lo que una verdadera solución de navegador cruzado es más difícil.

2

Si tiene que soportar los dispositivos que soportan el ratón y el tacto, otra solución es utilizar un detector de eventos de captura que detiene todos los eventos de ratón que se producen ya sea

  • en un plazo después del evento táctil
  • en la misma posición como el evento táctil
  • en el mismo elemento de destino como el evento táctil

la información (tiempo, posición o elemento de destino) de la ev táctil ent se puede grabar en otro oyente de eventos de captura.

0

Envolver el código del mouse solo en una función Window.matchesMedia es la forma más limpia que encontré.

if (window.matchMedia('(hover: hover), (any-hover: hover), (-moz-touch-enabled: 0)').matches) { 
    el.addEventListener('mouseover', ev => { 
     // mouse handler, no simulated hover 
    } 
} 

Esto funciona para evitar los ataques simulados, pero es probable que también evite los clics simulados.

Nota: -moz-touch habilitado parte necesaria en Firefox desde la versión 58.

Cuestiones relacionadas