javascript - eventos - on touch event html5
Detectando que el navegador no tiene mouse y es táctil (21)
Estoy desarrollando una aplicación web (no un sitio web con páginas de texto interesante) con una interfaz muy diferente para el tacto (su dedo oculta la pantalla cuando hace clic) y el mouse (depende en gran medida de la vista previa del posicionamiento). ¿Cómo puedo detectar que mi usuario no tiene mouse para presentarle la interfaz correcta? Planeo dejar un interruptor para personas con mouse y touch (como algunos cuadernos).
La capacidad de evento táctil en el navegador no significa que el usuario esté usando un dispositivo táctil (por ejemplo, Modernizr no lo corta). El código que responde correctamente la pregunta debería devolver falso si el dispositivo tiene un mouse, de lo contrario es cierto. Para dispositivos con mouse y touch, debería devolver false (no solo táctil)
Como nota al margen, mi interfaz táctil también podría ser adecuada para dispositivos de solo teclado, por lo que es más la falta de mouse que estoy buscando detectar.
Para hacer la necesidad más clara, aquí está la API que estoy buscando implementar:
// Level 1
// The current answers provide a way to do that.
hasTouch();
// Returns true if a mouse is expected.
// Note: as explained by the OP, this is not !hasTouch()
// I don''t think we have this in the answers already, that why I offer a bounty
hasMouse();
// Level 2 (I don''t think it''s possible, but maybe I''m wrong, so why not asking)
// callback is called when the result of "hasTouch()" changes.
listenHasTouchChanges(callback);
// callback is called when the result of "hasMouse()" changes.
listenHasMouseChanges(callback);
Me gustaría recomendar un script que me haya ayudado:
Había leído y probado todo lo sugerido, sin suficientes resultados.
Luego investigué un poco más y encontré este código - device.js
Estoy usando esto en el sitio web de mi cliente, para detectar la existencia del mouse:
( <html>
debería tener una clase de desktop
) y parece bastante bueno, y para el soporte touch
, solo realizo la comprobación regular ''ontouchend'' in document
y uso la información de ambas detecciones para asumir una cosa específica que necesito.
¿Por qué no solo detecta si tiene la capacidad de sentir toques y / o reaccionar a los movimientos del mouse?
// This will also return false on
// touch-enabled browsers like Chrome
function has_touch() {
return !!(''ontouchstart'' in window);
}
function has_mouse() {
return !!(''onmousemove'' in window);
}
¿Qué tal escuchar un evento mousemove en el documento? Luego, hasta que escuche ese evento, asume que el dispositivo solo está en contacto o con el teclado.
var mouseDetected = false;
function onMouseMove(e) {
unlisten(''mousemove'', onMouseMove, false);
mouseDetected = true;
// initializeMouseBehavior();
}
listen(''mousemove'', onMouseMove, false);
(Donde listen
y delegue sin ser unlisten
en addEventListener
o attachEvent
según corresponda).
Esperemos que esto no conduzca a demasiado jank visual, sería desagradable si necesita re-diseños masivos basados en el modo ...
@SamuelRossille No hay navegadores de los que tenga conocimiento que exponen la existencia (o la falta de) un mouse, desafortunadamente.
Entonces, dicho esto, solo tenemos que intentar hacer lo mejor que podamos con nuestra opción existente ... eventos. Sé que no es exactamente lo que estás buscando ... estuvo de acuerdo en que actualmente está lejos de ser ideal.
Podemos hacer nuestro mejor esfuerzo para averiguar si un usuario está usando un mouse o toca en cualquier momento dado. Aquí hay un ejemplo rápido y sucio usando jQuery & Knockout:
//namespace
window.ns = {};
// for starters, we''ll briefly assume if touch exists, they are using it - default behavior
ns.usingTouch = ko.observable(Modernizr.touch); //using Modernizr here for brevity. Substitute any touch detection method you desire
// now, let''s sort out the mouse
ns.usingMouse = ko.computed(function () {
//touch
if (ns.usingTouch()) {
//first, kill the base mousemove event
//I really wish browsers would stop trying to handle this within touch events in the first place
window.document.body.addEventListener(''mousemove'', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
}, true);
//remove mouse class from body
$(''body'').removeClass("mouse");
//switch off touch listener
$(document).off(".ns-touch");
// switch on a mouse listener
$(document).on(''mousemove.ns-mouse'', function (e) {
if (Math.abs(window.lastX - e.clientX) > 0 || window.lastY !== e.clientY) {
ns.usingTouch(false); //this will trigger re-evaluation of ns.usingMouse() and result in ns.usingMouse() === true
}
});
return false;
}
//mouse
else {
//add mouse class to body for styling
$(''body'').addClass("mouse");
//switch off mouse listener
$(document).off(".ns-mouse");
//switch on a touch listener
$(document).on(''touchstart.ns-touch'', function () { ns.usingTouch(true) });
return true;
}
});
//tests:
//ns.usingMouse()
//$(''body'').hasClass(''mouse'');
Ahora puede vincular / suscribirse a usingMouse () & usingTouch () y / o al estilo de su interfaz con la clase body.mouse . La interfaz cambiará de un lado a otro tan pronto como se detecte el cursor del mouse y en el inicio táctil.
Esperamos tener pronto mejores opciones de los proveedores de navegadores.
Acabo de encontrar una solución que creo que es bastante elegante.
// flag as mouse interaction by default
var isMouse = true;
// detect a touch event start and flag it
$(window).on(''touchstart'', function () {
// if triggers touchstart, toggle flag
// since touch events come before mouse event / click
// callback of mouse event will get in callback
// `isMouse === false`
isMouse = false;
});
// now the code that you want to write
// should also work with `mouse*` events
$(''a.someClass'').on(''click'', function () {
if (isMouse) {
// interaction with mouse event
// ...
} else {
// reset for the next event detection
// this is crucial for devices that have both
// touch screen and mouse
isMouse = true;
// interaction with touch event
// ...
}
});
Como han señalado otros, detectar definitivamente si tienen o no un mouse no es confiable. Esto puede cambiar fácilmente, dependiendo del dispositivo. Definitivamente es algo que no se puede hacer de manera confiable con un booleano verdadero o falso, al menos en una escala de documento.
Los eventos táctiles y los eventos del mouse son exclusivos. Entonces, esto puede ayudar un tanto en tomar diferentes acciones. El problema es que los eventos táctiles están más cerca de los eventos de mouse arriba / abajo / mover y también activan un evento de clic.
A partir de tu pregunta, dices que quieres tener un hover para obtener una vista previa. Más allá de eso, no conozco ningún otro detalle sobre tu interfaz. Supongo que con la falta de un mouse quieres un toque para obtener una vista previa, mientras que un clic hace una acción diferente debido a la vista previa de desplazamiento.
Si ese es el caso, puede adoptar un enfoque un tanto perezoso para la detección:
Un evento onclick siempre estará precedido por un evento onmouseover con un mouse. Por lo tanto, tome nota de que el mouse está encima del elemento que se ha presionado.
Puede hacer esto con un evento onmousemove en todo el documento. Puede usar event.target
para registrar en qué elemento reside el mouse. Luego, dentro de los eventos onclick puede verificar si el mouse está realmente sobre el elemento al que se hace clic (o un elemento secundario del elemento).
Desde allí, puede elegir entre confiar en el evento de clic para ambos y tomar una acción A o B según el resultado. La acción B podría no ser nada si algunos dispositivos táctiles no emiten un evento de clic (en cambio, debería confiar en los eventos ontouch *).
Como planea ofrecer una forma de cambiar entre las interfaces de todos modos, ¿sería factible simplemente pedirle al usuario que haga clic en un enlace o un botón para "ingresar" la versión correcta de la aplicación? Entonces podrías recordar su preferencia por futuras visitas. No es de alta tecnología, pero es 100% confiable :-)
Cuando Media Queries Level 4 está disponible en navegadores, podremos usar las consultas "pointer" y "hover" para detectar dispositivos con un mouse.
Si realmente queremos comunicar esa información a Javascript, podríamos usar una consulta CSS para establecer estilos específicos de acuerdo con el tipo de dispositivo, y luego usar getComputedStyle
en Javascript para leer ese estilo, y deducir el tipo de dispositivo original de él.
Pero un mouse se puede conectar o desconectar en cualquier momento, y el usuario puede querer cambiar entre el tacto y el mouse. Por lo tanto, es posible que necesitemos detectar este cambio y ofrecerle cambiar la interfaz o hacerlo de manera automática.
Eche un vistazo a modenizr una de sus características es táctil
http://modernizr.com/docs/#features-misc
Si bien no lo he probado completamente, parece funcionar muy bien
También está vinculado desde la página de modernizr http://www.html5rocks.com/en/mobile/touchandmouse/
El principal problema es que tiene las siguientes clases diferentes de dispositivos / casos de uso:
- Mouse y teclado (escritorio)
- Toque solo (teléfono / tableta)
- Ratón, teclado y tacto (touch laptops)
- Toque y teclado (teclado bluetooth en tableta)
- Solo mouse (usuario deshabilitado / preferencia de exploración)
- Solo teclado (Usuario deshabilitado / preferencia de exploración)
- Toque y mouse (es decir, eventos de desplazamiento desde el lápiz Galaxy Note 2)
Lo que es peor, es que uno puede hacer la transición de algunas de estas clases a otras (conecta un mouse, se conecta al teclado), o un usuario puede APARECER para estar en una computadora portátil normal hasta que se acerque y toque la pantalla.
Tiene razón al suponer que la presencia de constructores de eventos en el navegador no es una buena forma de avanzar (y es algo inconsistente). Además, a menos que esté rastreando un evento muy específico o solo tratando de descartar algunas de las clases anteriores, el uso de los eventos en sí no es una prueba completa.
Por ejemplo, digamos que ha descubierto que un usuario ha emitido un mousemove real (no el falso de eventos táctiles, consulte http://www.html5rocks.com/en/mobile/touchandmouse/ ).
¿Y que?
¿Habilita los estilos de desplazamiento? Agrega más botones?
De cualquier manera, estás aumentando el tiempo para el vidrio porque tienes que esperar a que se desate un evento.
Pero entonces, ¿qué sucede cuando tu noble usuario decide que quiere desconectar su mouse y entrar en contacto completo ... esperas que toque tu interfaz ahora abarrotada, y luego la cambie justo después de que haya hecho el esfuerzo de identificar tu UI abarrotada?
En forma de viñeta, citando stucox en https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101
- Queremos detectar la presencia de un mouse
- Ae probablemente no pueda detectar antes de que se active un evento
- Como tal, lo que estamos detectando es si se ha utilizado un mouse en esta sesión; no será inmediatamente desde la carga de la página.
- Probablemente tampoco podamos detectar que no hay un mouse, sería indefinido hasta que sea verdadero (creo que esto tiene más sentido que establecerlo falso hasta que se demuestre)
- Y probablemente no podamos detectar si un mouse está desconectado a mitad de la sesión, eso será indistinguible del usuario que acaba de darse por vencido con el mouse
Un lado: el navegador SÉ cuando un usuario conecta un mouse / se conecta a un teclado, pero no lo expone a JavaScript ... ¡dang!
Esto debería llevarte a lo siguiente:
El seguimiento de las capacidades actuales de un usuario dado es complejo, poco confiable y de mérito dudoso
La idea de mejora progresiva se aplica bastante bien aquí, sin embargo. Cree una experiencia que funcione sin problemas, sin importar el contexto del usuario. Luego haga suposiciones basadas en las características del navegador / consultas de medios para agregar funcionalidad que será relativa en el contexto supuesto. La presencia de un mouse es solo una de las muchas maneras en que diferentes usuarios en diferentes dispositivos experimentan su sitio web. Cree algo con mérito en su kernel y no se preocupe demasiado por cómo las personas hacen clic en los botones.
El principal problema que veo aquí es que la mayoría de los dispositivos táctiles disparan un evento de mouse junto con el toque correspondiente (touchstart -> mousedown, touchmove -> mousemove, etc.). Para los teclados únicos, por último para los modernos, tienen un navegador genérico, por lo que ni siquiera se puede detectar la presencia de la clase MouseEvent.
La solución menos dolorosa aquí sería, en mi opinión, mostrar un menú en el lanzamiento (con administración ''alt'' para los usuarios que solo usan el teclado) y quizás almacenar la opción con localStorage / cookies / serverside o de lo contrario, mantener la opción salme el próximo tiempo que el visitante viene.
En general, es una mejor idea detectar si la función de mouseover es compatible en lugar de detectar el tipo de sistema operativo / navegador. Puedes hacer eso simplemente por lo siguiente:
if (element.mouseover) {
//Supports the mouseover event
}
Asegúrese de no hacer lo siguiente:
if (element.mouseover()) {
// Supports the mouseover event
}
Este último realmente llamaría al método, en lugar de verificar su existencia.
Puede leer más aquí: http://www.quirksmode.org/js/support.html
Esto funcionó para mí en una situación similar. Básicamente, suponga que el usuario no tiene un mouse hasta que vea una serie corta de mousemoves consecutivos, sin mousedowns o mouseups intermedios. No es muy elegante, pero funciona.
var mousedown = false;
var mousemovecount = 0;
function onMouseDown(e){
mousemovecount = 0;
mousedown = true;
}
function onMouseUp(e){
mousedown = false;
mousemovecount = 0;
}
function onMouseMove(e) {
if(!mousedown) {
mousemovecount++;
if(mousemovecount > 5){
window.removeEventListener(''mousemove'', onMouseMove, false);
console.log("mouse moved");
$(''body'').addClass(''has-mouse'');
}
} else {
mousemovecount = 0;
}
}
window.addEventListener(''mousemove'', onMouseMove, false);
window.addEventListener(''mousedown'', onMouseDown, false);
window.addEventListener(''mouseup'', onMouseUp, false);
La mejor idea en mi opinión es el oyente mousemove
(actualmente la respuesta principal). Creo que este método necesita ser modificado un poco. Es cierto que los navegadores táctiles emulan incluso el evento mousemove, como puede ver en esta discusión sobre iOS , por lo que debemos tener un poco de cuidado.
Tiene sentido que los navegadores táctiles solo emulen este evento cuando el usuario toca la pantalla (el dedo del usuario está abajo). Esto significa que debemos agregar una prueba durante nuestro manejador de mousemove para ver qué botón del mouse está inactivo (si hay alguno) durante el evento. Si no hay un botón del mouse caído, podemos suponer con seguridad que hay un mouse real presente. Si el botón del mouse está desactivado, la prueba no es concluyente.
Entonces, ¿cómo se implementaría esto? Esta question muestra que el método más confiable para examinar qué botón del mouse está inactivo durante un mousemove es escuchar 3 eventos en el nivel del documento: mousemove, mousedown y mouseup. El arriba y el abajo solo establecerán un indicador booleano global. El movimiento realizará la prueba. Si tienes un movimiento y el valor booleano es falso, podemos suponer que hay un mouse presente. Ver pregunta para ejemplos de código exactos.
Un último comentario. Esta prueba no es ideal porque no se puede realizar en tiempo de carga. Por lo tanto, usaría un método de mejora progresiva como se sugirió anteriormente. De forma predeterminada, muestra una versión que no es compatible con la interfaz de desplazamiento del mouse específico. Si se descubre un mouse, habilite este modo en tiempo de ejecución usando JS. Esto debería aparecer tan transparente como sea posible para el usuario.
Para admitir cambios en la configuración del usuario (es decir, el mouse se ha desconectado), puede volver a realizar pruebas periódicamente. Aunque creo que será mejor en este caso, simplemente notificar al usuario acerca de los 2 modos y dejar que los usuarios cambien manualmente entre ellos (al igual que la opción móvil / de escritorio que siempre se puede revertir).
La respuesta de @ Wyatt es excelente y nos da mucho en qué pensar.
En mi caso, elegí escuchar la primera interacción, para luego establecer un comportamiento. Entonces, incluso si el usuario tiene un mouse, lo trataré como un dispositivo táctil si la primera interacción fue un toque.
Teniendo en cuenta el orden en el que se procesan los eventos :
- touchstart
- touchmove
- touchend
- ratón sobre
- movimiento del ratón
- ratón hacia abajo
- mouseup
- hacer clic
Podemos suponer que si el evento del mouse se activa antes de tocar, es un evento real del mouse, no uno emulado. Ejemplo (usando jQuery):
$(document).ready(function() {
var $body = $(''body'');
var detectMouse = function(e){
if (e.type === ''mousedown'') {
alert(''Mouse interaction!'');
}
else if (e.type === ''touchstart'') {
alert(''Touch interaction!'');
}
// remove event bindings, so it only runs once
$body.off(''mousedown touchstart'', detectMouse);
}
// attach both events to body
$body.on(''mousedown touchstart'', detectMouse);
});
Eso funcionó para mí
No creo que sea posible identificar un dispositivo solo táctil (que yo sepa, por supuesto). El problema principal es que todos los eventos de mouse y teclado son activados por dispositivos táctiles también. Vea el siguiente ejemplo, ambas alertas devuelven verdadero para dispositivos táctiles.
function is_touch_present() {
return (''ontouchstart'' in window) || (''onmsgesturechange'' in window);
}
function is_mouse_present() {
return ((''onmousedown'' in window) && (''onmouseup'' in window) && (''onmousemove'' in window) && (''onclick'' in window) && (''ondblclick'' in window) && (''onmousemove'' in window) && (''onmouseover'' in window) && (''onmouseout'' in window) && (''oncontextmenu'' in window));
}
alert("Touch Present: " + is_touch_present());
alert("Mouse Present: " + is_mouse_present());
Realicé algunas pruebas en varios PC, Linux, iPhone, teléfonos Android y pestañas. Es extraño que no haya una solución fácil a prueba de balas. El problema surge cuando algunos que tienen Touch y ningún mouse todavía presentan eventos de Touch and Mouse para la aplicación. Como quiero admitir instancias solo de mouse y de solo toque, quiero procesar ambas, pero esto provoca apariciones dobles de interacciones de usuario. Si puede saber que el mouse no está presente en el dispositivo, puede ignorar los eventos de mouse falsos / insertados. Intentó establecer la marca si se encuentra MouseMove, pero algunos navegadores lanzan MouseMove falso, MouseUp y MouseDown. Intenté examinar las marcas de tiempo pero pensé que esto era demasiado arriesgado. En pocas palabras: encontré que los navegadores que crearon los eventos falsos del mouse siempre insertaron un solo MouseMove justo antes de MouseDown insertado. En el 99.99% de mis casos, cuando se ejecuta en un sistema que tiene un mouse real, hay múltiples eventos MouseMove consecutivos, al menos dos. Por lo tanto, realice un seguimiento de si el sistema encuentra dos eventos MouseMove consecutivos y declara que no hay un mouse presente si esta condición nunca se cumple. Esto es probablemente demasiado simple, pero está trabajando en todas mis configuraciones de prueba. Creo que me quedaré con él hasta que encuentre una mejor solución. - Jim W
Recomiendo encarecidamente en contra de este enfoque. Tenga en cuenta la pantalla táctil, los dispositivos de tamaño de escritorio, y usted tiene un conjunto diferente de problemas para resolver.
Por favor haz que tu aplicación sea utilizable sin un mouse (es decir, sin vista previa).
Solo es posible detectar si un navegador tiene capacidad táctil. No hay forma de saber si realmente tiene una pantalla táctil o un mouse conectado.
Sin embargo, uno puede priorizar el uso escuchando el evento táctil en lugar del evento del mouse si se detecta la capacidad táctil.
Para detectar la capacidad táctil entre navegadores:
function hasTouch() {
return ((''ontouchstart'' in window) || // html5 browsers
(navigator.maxTouchPoints > 0) || // future IE
(navigator.msMaxTouchPoints > 0)); // current IE10
}
Entonces uno puede usar esto para verificar:
if (!hasTouch()) alert(''Sorry, need touch!);
o para elegir qué evento escuchar, ya sea:
var eventName = hasTouch() ? ''touchend'' : ''click'';
someElement.addEventListener(eventName , handlerFunction, false);
o use enfoques por separado para tocar y no tocar:
if (hasTouch() === true) {
someElement.addEventListener(''touchend'' , touchHandler, false);
} else {
someElement.addEventListener(''click'' , mouseHandler, false);
}
function touchHandler(e) {
/// stop event somehow
e.stopPropagation();
e.preventDefault();
window.event.cancelBubble = true;
// ...
return false; // :-)
}
function mouseHandler(e) {
// sorry, touch only - or - do something useful and non-restrictive for user
}
Para el mouse uno solo puede detectar si el mouse está siendo utilizado, no si existe o no. Uno puede configurar un indicador global para indicar que el mouse fue detectado por el uso (similar a una respuesta existente, pero simplificada un poco):
var hasMouse = false;
window.onmousemove = function() {
hasMouse = true;
}
(uno no puede incluir mouseup
o mousedown
ya que estos eventos también pueden ser activados por touch)
Los navegadores restringen el acceso a las API del sistema de bajo nivel, que es necesario para poder detectar características como las capacidades de hardware del sistema en el que se está utilizando.
Existe la posibilidad de quizás escribir un plugin / extensión para acceder a estos, pero a través de JavaScript y DOM dicha detección es limitada para este fin y uno tendría que escribir un plugin específico para las diversas plataformas del sistema operativo.
Entonces, en conclusión: tal detección solo puede estimarse mediante una "buena conjetura".
Una solución simple en jQuery para detectar el uso del mouse, que resuelve el problema donde los dispositivos móviles también activan el evento ''mousemove''. Simplemente agregue un detector de inicio táctil para eliminar el detector de movimiento del mouse, para que no se active al tocar.
$(''body'').one(''touchstart.test'', function(e) {
// Remove the mousemove listener for touch devices
$(''body'').off(''mousemove.test'');
}).one(''mousemove.test'', function(e) {
// MOUSE!
});
Por supuesto, el dispositivo todavía podría ser táctil Y el mouse, pero lo anterior garantizará que se usó un mouse real.
Tera-WURFL puede decirle las capacidades del dispositivo que está visitando su sitio comparando la firma del navegador con su base de datos. Dale un vistazo, es gratis!