Detectando que el navegador no tiene mouse y es solo táctil

149

Estoy desarrollando una aplicación web (no un sitio web con páginas de texto interesante) con una interfaz táctil muy diferente (su dedo oculta la pantalla cuando hace clic) y el mouse (depende en gran medida de la vista previa de desplazamiento). ¿Cómo puedo detectar que mi usuario no tiene mouse para presentarle la interfaz correcta? Tengo la intención de dejar un interruptor para las personas con el mouse y el tacto (como algunos portátiles).

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 a la pregunta debería devolver falso si el dispositivo tiene un mouse, de lo contrario, verdadero. Para dispositivos con mouse y touch, debería devolver false (no solo touch)

Como nota al margen, mi interfaz táctil también puede ser adecuada para dispositivos con solo teclado, por lo que es más la falta de mouse que estoy buscando detectar.

Para aclarar la necesidad, 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);
nraynaud
fuente
Creo que debe repensar su diseño si desea que una aplicación sea aplicable tanto para computadoras de escritorio como móviles / táctiles, pero que tenga comportamientos diferentes para cada una. No creo que lo que buscas sea realmente posible en este momento, ya que una búsqueda rápida en Google para "javascript detect mouse" muestra una publicación moderadamente útil en quirksmode.org para detectar varios estados del mouse (clics, posición, etc.), pero CERO da como resultado si el mouse existe o no.
davethegr8
24
Tal vez sea porque Google no ayudó que lo pregunté aquí.
nraynaud
2
¿Has probado document mouseenter de jquery? $ (document) .mouseenter (function (e) {alert ("mouse");});
Parag Gajjar
44
Después de considerar casi una docena de caminos prometedores solo para rechazar cada uno en cuestión de minutos, esta pregunta me está volviendo espléndidamente loco.
Jordan Gray

Respuestas:

65

El principal problema es que tiene las siguientes clases diferentes de dispositivos / casos de uso:

  1. Ratón y teclado (escritorio)
  2. Solo toque (teléfono / tableta)
  3. Mouse, teclado y touch (laptops táctiles)
  4. Tacto y teclado (teclado bluetooth en tableta)
  5. Solo mouse (usuario deshabilitado / preferencia de navegación)
  6. Solo teclado (usuario deshabilitado / preferencia de navegación)
  7. Toque y mouse (es decir, eventos de desplazamiento del 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 puede aparecer un usuario en una computadora portátil normal hasta que alcancen y toquen la pantalla.

Tiene razón al suponer que la presencia de constructores de eventos en el navegador no es una buena manera de avanzar (y es algo inconsistente). Además, a menos que esté rastreando un evento muy específico o solo esté tratando de descartar algunas clases anteriores, el uso de eventos en sí no es una prueba completa.

Por ejemplo, supongamos que descubrió que un usuario ha emitido un movimiento real del mouse (no el falso de los eventos táctiles, consulte http://www.html5rocks.com/en/mobile/touchandmouse/ ).

¿Y que?

¿Habilitas los estilos de desplazamiento? ¿Agregas más botones?

De cualquier manera, está aumentando el tiempo de cristal porque tiene que esperar a que se active un evento.

Pero entonces, ¿qué sucede cuando su noble usuario decide que quiere desconectar su mouse e ir a tocarlo por completo?

En forma de viñeta, citando stucox en https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Queremos detectar la presencia de un mouse
  • Es probable que Ae 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: estaría indefinido hasta que sea verdadero (creo que esto tiene más sentido que configurarlo como falso hasta que se pruebe)
  • Y probablemente no podamos detectar si un mouse está desconectado en la mitad de la sesión, eso será indistinguible de que el usuario simplemente se rinda con su mouse

Un aparte: el navegador SÍ sabe cuándo un usuario conecta un mouse / se conecta a un teclado, pero no lo expone a JavaScript ... ¡dang!

Esto debería llevarlo a lo siguiente:

El seguimiento de las capacidades actuales de un usuario determinado es complejo, poco confiable y de dudoso mérito

Sin embargo, la idea de mejora progresiva se aplica bastante bien aquí. 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 funcionalidades que serán relativas en el contexto asumido. La presencia de un mouse es solo una de las múltiples formas en que diferentes usuarios en diferentes dispositivos experimentan su sitio web. Cree algo con mérito en su núcleo y no se preocupe demasiado por cómo la gente hace clic en los botones.

Wyatt
fuente
3
gran respuesta. ¡Con suerte, el usuario siempre tiene una pantalla! Creo que tiene sentido construir una interfaz que se ajuste al modo de interacción actual del usuario. En una computadora portátil táctil, tiene sentido ajustar la aplicación (es decir, los :hoverelementos y cosas así) cuando el usuario cambia de mouse a táctil. Parece poco probable que el usuario esté usando el mouse + touch exactamente al mismo tiempo (quiero decir, como es como tener 2 ratones conectados a la misma computadora jajaja)
Sebastien Lorber
@SebastienLorber: odio decírtelo, pero los usuarios no siempre tienen una pantalla. ( ¿Es posible usar JavaScript para detectar si un lector de pantalla se está ejecutando en la máquina de un usuario? )
ashleedawg
57

¿Qué tal escuchar un evento de mousemove en el documento? Luego, hasta que escuche ese evento, asuma que el dispositivo es solo táctil o de teclado.

var mouseDetected = false;
function onMouseMove(e) {
  unlisten('mousemove', onMouseMove, false);
  mouseDetected = true;
  // initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);

(Donde listeny unlistendelegar addEventListenerao attachEventsegún corresponda)

Esperemos que esto no conduzca a demasiado jank visual, sería una mierda si necesita rediseños masivos basados ​​en el modo ...

Dan
fuente
2
Es una buena idea, pero desafortunadamente la demora en la respuesta hará que sea inutilizable cuando la interfaz de usuario de la aplicación dependa de si hay un mouse disponible. Esto es especialmente cierto si la aplicación puede ser iframed, por lo que los eventos del mouse solo lo golpearán si el el mouse se mueve sobre el iframe ..
Jon Gjengset
77
Esto podría funcionar si la aplicación comienza con una pantalla de bienvenida y un botón "continuar". Si el mouse se mueve antes del primer evento de mousedown, entonces tienes un mouse. Solo fallaría si el botón se carga directamente debajo del mouse y el usuario tiene una mano muy firme (incluso se debe levantar 1 píxel).
SpliFF
49
Buena idea, pero no parece funcionar en nuestras pruebas . Los iPads desencadenan este evento.
Jeff Atwood
3
@JeffAtwood, ¿qué terminaste haciendo en tu caso?
Michael Haren
2
Los iPads definitivamente activan el evento mousemove, justo antes del evento mousedown. He descubierto que el recuento de mousedown> 0 y el recuento de mousedown == recuento de mousemove son una buena forma de no detectar ningún mouse. No puedo duplicar esto con un mouse real.
Peter Wooster
36

A partir de 2018, hay una forma buena y confiable de detectar si un navegador tiene un mouse (o dispositivo de entrada similar): características de interacción de medios CSS4 que ahora son compatibles con casi cualquier navegador moderno (excepto IE 11 y navegadores móviles especiales).

W3C:

La función de medios de puntero se utiliza para consultar la presencia y precisión de un dispositivo señalador como un mouse.

Ver las siguientes opciones:

    /* The primary input mechanism of the device includes a 
pointing device of limited accuracy. */
    @media (pointer: coarse) { ... }

    /* The primary input mechanism of the device 
includes an accurate pointing device. */
    @media (pointer: fine) { ... }

    /* The primary input mechanism of the 
device does not include a pointing device. */
    @media (pointer: none) { ... }

    /* Primary input mechanism system can 
       hover over elements with ease */
    @media (hover: hover) { ... }

    /* Primary input mechanism cannot hover 
       at all or cannot conveniently hover 
       (e.g., many mobile devices emulate hovering
       when the user performs an inconvenient long tap), 
       or there is no primary pointing input mechanism */
    @media (hover: none) { ... }

    /* One or more available input mechanism(s) 
       can hover over elements with ease */
    @media (any-hover: hover) { ... }


    /* One or more available input mechanism(s) cannot 
       hover (or there are no pointing input mechanisms) */
    @media (any-hover: none) { ... }

Las consultas de medios también se pueden usar en JS:

if(window.matchMedia("(any-hover: none)").matches) {
    // do sth
}

Relacionado:

Documentación de W3: https://www.w3.org/TR/mediaqueries-4/#mf-interaction

Soporte de navegador: https://caniuse.com/#search=media%20features

Problema similar: detectar si un dispositivo cliente admite: hover y: estados de enfoque

Blackbam
fuente
Personalmente me gusta esta respuesta, pero a partir de ahora (10/19), las consultas CSS de puntero y puntero @media solo están disponibles en ~ 85% de los dispositivos en todo el mundo según caniuse.com. Ciertamente no está mal, es preferible el 95% o más. Esperemos que esto se convierta en estándar en los dispositivos pronto.
MQuiggGeorgia
1
@MQuiggGeorgia Básicamente estoy de acuerdo con su crítica, básicamente, todavía no es compatible en todas partes. Todavía caniuse.com para mí dice que es compatible con 91.2% ( caniuse.com/#feat=css-media-interaction ). Mirando más de cerca, es compatible en todas partes, excepto IE 11 y navegadores especiales aplanados en dispositivos móviles. Para ser justos, esto es cierto para cualquier característica moderna, ya que Microsoft dejó de implementar las características de IE hace mucho tiempo. Para IE 11, puede usar un respaldo de las otras respuestas aquí.
Blackbam
23

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, trataré como dispositivo táctil si la primera interacción fue táctil.

Teniendo en cuenta el orden dado en el que se procesan los eventos :

  1. inicio táctil
  2. touchmove
  3. touchend
  4. ratón sobre
  5. movimiento del ratón
  6. ratón hacia abajo
  7. mouseup
  8. hacer clic

Podemos suponer que si el evento del mouse se dispara antes del toque, 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 funciono para mi

Hugo Silva
fuente
No funciona para mí, Ipad Safari (IOS8.3) también detecta un mouse con este fragmento
netzaffin
3
@netzaffin. Gracias por los comentarios, encontré que es más consistente usando mousedown en lugar de mouseover. ¿Podrías echar un vistazo a este violín desde tu IOS y decirme el resultado? Saludos jsfiddle.net/bkwb0qen/15/embedded/result
Hugo Silva
1
Si tiene una pantalla táctil con un mouse, solo se detectará el método de entrada utilizado primero.
0xcaff
11

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, se puede priorizar el uso al escuchar 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 separados para tocar versus 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 se está usando el mouse, no si existe o no. Se puede configurar un indicador global para indicar que el mouse fue detectado por el uso (similar a una respuesta existente, pero simplificado un poco):

var hasMouse = false;

window.onmousemove = function() {
    hasMouse = true;
}

(no se puede incluir mouseupo mousedownya que estos eventos también se pueden activar al tocarlos)

Los navegadores restringen el acceso a las API de sistema de bajo nivel que se necesitan 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 complemento / extensión para acceder a estos, pero a través de JavaScript y DOM, dicha detección es limitada para este propósito y uno tendría que escribir un complemento específico para las diversas plataformas del sistema operativo.

En conclusión: tal detección solo puede estimarse mediante una "buena suposición".


fuente
8

Cuando Media Queries Level 4 está disponible en los navegadores, podremos usar las consultas "puntero" 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 getComputedStyleen Javascript para leer ese estilo y derivar 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 ofrecer cambiar la interfaz o hacerlo automáticamente.

joeytwiddle
fuente
1
Específicamente, any-pointeryany-hover le permitirá investigar todas las capacidades aplicables del dispositivo. ¡Es bueno echar un vistazo a cómo podríamos resolver este problema en el futuro! :)
Jordan Gray
2
window.matchMedia ("(cualquier puntero: grueso)"). coincide === verdadero?
4esn0k
7

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ía recordar su preferencia para futuras visitas. No es de alta tecnología, pero es 100% confiable :-)

Brandan
fuente
2
En realidad es una sugerencia bastante buena, pero retrasa el tiempo antes de que el usuario acceda a la interfaz real. Además, tendré que proporcionar una forma de cambiar después de la elección inicial. Termina siendo más trabajo que si simplemente pudiera detectarse ...
Jon Gjengset
1
Preguntar al usuario es claramente la mejor manera, si no siempre infalible, y le brinda un lugar conveniente para colocar notificaciones de actualización y qué no. Creo que has terminado de pensar en el "problema" ..
T4NK3R
4

@SamuelRossille No hay navegadores que yo sepa que expongan la existencia (o la falta de ella) de un mouse, desafortunadamente.

Entonces, dicho esto, solo tenemos que intentar y hacer lo mejor que podamos con nuestra opción existente ... eventos. Sé que no es exactamente lo que estás buscando ... de acuerdo, actualmente está lejos de ser ideal.

Podemos hacer todo lo posible para determinar si un usuario está usando un mouse o táctil en un momento dado. Aquí hay un ejemplo rápido y sucio con 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 enlazar / suscribirse a usingMouse () y usingTouch () y / o diseñar su interfaz con la clase body.mouse . La interfaz cambiará de un lado a otro tan pronto como se detecte un cursor del mouse y al inicio táctil.

Esperemos que pronto tengamos mejores opciones de los proveedores de navegadores.

revs daredev
fuente
2

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!

Gigi
fuente
1
Esto no funcionará para dispositivos que pueden o no tener pantallas táctiles y un mouse. Por ejemplo, una computadora Windows de escritorio puede estar conectada a una pantalla táctil, pero generalmente también tendrá un mouse, mientras que una tableta también puede estar ejecutando Windows, pero puede que no tenga un mouse conectado ...
Jon Gjengset
@Jonhoo Solo asuma que los sistemas operativos de escritorio tienen un mouse conectado. Después de todo, deben admitir una amplia gama de software que no se desarrolló con una pantalla táctil en mente.
Gigi
1
¿Qué pasa con las tabletas que ejecutan Windows 8 sin formato? O Linux? ¿O computadoras portátiles con Android?
Jon Gjengset
2
@Jonhoo Obviamente, este enfoque es menos que óptimo, pero no hay una forma portátil de saberlo (todavía). Si uno está ejecutando una computadora portátil con Android, solo suponga que tiene capacidad táctil. Si uno está ejecutando una tableta con Windows 8, suponga que es compatible con el mouse (el sistema operativo debe emular el mouse para programas no táctiles).
Gigi
Esto ahora está tan desactualizado que ya no es relevante.
Ingeniero el
2

¿Por qué no 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);
}
iblue
fuente
44
Debido a que algunos navegadores (IE9, por ejemplo) informan que la función existe incluso si nunca se activará. Creo que este también es el comportamiento "correcto".
Jon Gjengset
¿Por qué usarías una función? solo has_touch = 'ontouchstart' in windowserá suficiente, y así sucesivamente.
vsync
Bueno, funciona en Chrome 47 para OS X, al menos. No se informa sobre el inicio rápido.
phreakhead
2

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 movimientos consecutivos del mouse, sin intervenir mousedowns o mouseups. No 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);
Michael Condouris
fuente
0

Como otros han señalado, detectar definitivamente si tienen o no un mouse no es confiable. Esto puede cambiar fácilmente, dependiendo del dispositivo. Definitivamente es algo que no puede hacer de manera confiable con un booleano verdadero o falso, al menos en una escala de documentos.

Los eventos táctiles y los eventos del mouse son exclusivos. Entonces, esto puede ayudar de alguna manera a tomar diferentes acciones. El problema es que los eventos táctiles están más cerca de los eventos arriba / abajo / movimiento del mouse, y también desencadenan un evento de clic.

De tu pregunta, dices que quieres tener un cursor para previsualizar. Más allá de eso, no conozco otros detalles sobre su interfaz. Estoy asumiendo que con la falta de un ratón que desea un grifo de vista previa, mientras que un clic hace una acción diferente a causa de la vista previa de la libración.

Si ese es el caso, puede adoptar un enfoque un tanto vago para la detección:

Un evento onclick siempre estará precedido por un evento onmouseover con un mouse. Por lo tanto, tenga en cuenta que el mouse está encima del elemento en el que se ha hecho clic.

Puede hacer esto con un evento onmousemove en todo el documento. Puede usar event.targetpara registrar en qué elemento reside el mouse. Luego, dentro de sus eventos onclick, puede verificar si el mouse está realmente sobre el elemento en el que se hace clic (o un elemento secundario del elemento).

Desde allí, puede elegir 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 su lugar, tendría que confiar en los eventos ontouch *).

Luke
fuente
0

No creo que sea posible identificar dispositivos táctiles (que yo sepa, por supuesto). El problema principal es que todos los eventos de mouse y teclado también se activan con dispositivos táctiles. 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());
Bikas
fuente
2
Safari ipad regresa truepara'onmousedown' in window
vsync
0

La mejor idea en mi opinión es el mousemoveoyente (actualmente la respuesta principal). Creo que este método debe modificarse un poco. Es cierto que los navegadores táctiles emulan incluso el evento mousemove, como puede ver en esta discusión de 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á hacia abajo). Esto significa que debemos agregar una prueba durante nuestro controlador de movimiento del mouse para ver qué botón del mouse está presionado (si corresponde) durante el evento. Si no hay ningún botón del mouse presionado, podemos asumir con seguridad que hay un mouse real presente. Si un botón del mouse está presionado, la prueba no es concluyente.

Entonces, ¿cómo se implementaría esto? Esta pregunta muestra que el método más confiable para examinar qué botón del mouse está presionado durante un movimiento del mouse es escuchar realmente 3 eventos en el nivel del documento: mousemove, mousedown y mouseup. Arriba y abajo solo establecerán una bandera booleana global. El movimiento realizará la prueba. Si tiene un movimiento y el valor booleano es falso, podemos suponer que hay un mouse presente. Ver pregunta para ejemplos de código exacto.

Un comentario final ... 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 manera predeterminada, muestra una versión que no admite la interfaz de desplazamiento específico del mouse. Si se descubre un mouse, habilite este modo en tiempo de ejecución utilizando JS. Esto debería parecer lo más sencillo 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 sobre los 2 modos y dejar que los usuarios cambien manualmente entre ellos (al igual que la opción móvil / escritorio que siempre se puede revertir).

talkol
fuente
Gracias por sus buenas sugerencias de solución ... Creo que el problema principal no se resolverá. Tendré que recurrir a uno de estos
Samuel Rossille, el
Lamentablemente, mousemove se activa al hacer clic en un ipad. Solo probado con simulador. Para hasMouse () estaba usando if (! ('Ontouchstart' en la ventana)) return true; pero no funciona para laptops con soporte táctil.
Chris Gunawardena
@Chris G - iPad hell ... (golpeando mi cabeza contra la pared)
vsync
0

Ejecuté algunas pruebas en varias PC, Linux, iPhone, teléfonos Android y pestañas. Extraño que no haya una solución fácil a prueba de balas. El problema surge cuando algunos que tienen Touch y no mouse todavía presentan eventos Touch y Mouse a la aplicación. Dado que desea admitir instancias solo de mouse y solo de tacto, desea procesar ambas, pero esto provoca una doble aparición de interacciones del usuario. Si puede saber que el mouse no está presente en el dispositivo, puede ignorar los eventos falsos / insertados del mouse. Intenté configurar la marca si se encuentra MouseMove, pero algunos navegadores arrojan MouseMove falso, así como MouseUp y MouseDown. Intenté examinar las marcas de tiempo pero pensé que esto era demasiado arriesgado. En pocas palabras: los navegadores que crearon los eventos falsos del mouse siempre insertaron un solo MouseMove justo antes del MouseDown insertado. En el 99.99% de mis casos, cuando se ejecuta en un sistema que tiene un mouse real, hay varios eventos MouseMove consecutivos, al menos dos. Por lo tanto, realice un seguimiento de si el sistema encuentra dos eventos MouseMove consecutivos y declare que no hay mouse presente si esta condición nunca se cumple. Probablemente sea demasiado simple, pero funciona en todas mis configuraciones de prueba. Creo que me quedaré con eso hasta que encuentre una mejor solución. - Jim W

jwasch
fuente
0

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 oyente de inicio táctil para eliminar el oyente del mousemove, para que no se active al tocarlo.

$('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 aún podría ser táctil Y mouse, pero lo anterior garantizará que se usó un mouse real.

suncat100
fuente
0

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
    // ...
  }
});
Koala Yeung
fuente
0

Me encontré con el mismo problema, donde un solo toque también se registró como un clic. Después de leer los comentarios de las respuestas más votadas, se me ocurrió mi propia solución:

var body = document.getElementsByTagName('body')[0];
var mouseCount = 0;

// start in an undefined state 
// (i use this to blend in elements once we decide what input is used)
var interactionMode = 'undefined';


var registerMouse = function() {
  // count up mouseCount every time, the mousemove event is triggered
  mouseCount++;

  // but dont set it instantly. 
  // instead wait 20 miliseconds (seems to be a good value for multiple move actions), 
  // if another mousemove event accoures switch to mouse as interaction 
  setTimeout(function() {
    // a touch event triggers also exactly 1 mouse move event.
    // So only if mouseCount is higher than 1 we are really moving the cursor by mouse.
    if (mouseCount > 1) {
      body.removeEventListener('mousemove', registerMouse);
      body.removeEventListener('touchend', registerTouch);

      interactionMode = 'mouse';
      console.log('now mousing');
      listenTouch();
    }

    // set the counter to zero again
    mouseCount = 0;
  }, 20);
};

var registerTouch = function() {
  body.removeEventListener('mousemove', registerMouse);
  body.removeEventListener('touchend', registerTouch);

  interactionMode = 'touch';
  console.log('now touching');
  mouseCount = 0;

  listenMouse();
};

var listenMouse = function() {
  body.addEventListener("mousemove", registerMouse);
};
var listenTouch = function() {
  body.addEventListener("touchend", registerTouch);
};

listenMouse();
listenTouch();

// after one second without input, assume, that we are touching
// could be adjusted to several seconds or deleted
// without this, the interactionMode would stay 'undefined' until first mouse or touch event
setTimeout(function() {
  if (!body.classList.contains('mousing') || body.classList.contains('touching')) {
    registerTouch();
  }
}, 1000);
/* fix, so that scrolling is possible */

html,
body {
  height: 110%;
}
Mouse or touch me

El único problema que encontré es que debes poder desplazarte para detectar correctamente un evento táctil. una sola pestaña (táctil) puede causar problemas.

Jannis Nexor Ianus Betz
fuente
0

Pasé horas resolviendo este problema para mi aplicación Phonegap y se me ocurrió este truco. Genera una advertencia de consola si el evento desencadenado es un evento "pasivo", lo que significa que no activa ningún cambio, ¡pero funciona! Me interesaría cualquier idea para mejorar o un método mejor. Pero esto efectivamente me permite usar $ .touch () universalmente.

$(document).ready(function(){
  $("#aButton").touch(function(origElement, origEvent){
    console.log('Original target ID: ' + $(origEvent.target).attr('id'));
  });
});

$.fn.touch = function (callback) {
    var touch = false;
    $(this).on("click", function(e){
        if (!touch)
        {
            console.log("I click!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }else{
            touch = true;
        }
        touch = false;
    });
    $(this).on("touchstart", function(e){
        if (typeof e.touches != typeof undefined)
        {
            e.preventDefault();
            touch = true;
            console.log("I touch!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button id="aButton">A Button</button>

Arrendajo
fuente
0

El principal problema que veo aquí es que la mayoría de los dispositivos táctiles activan un evento del mouse junto con el correspondiente touch touch (inicio táctil -> mousedown, touchmove -> mousemove, etc.). Solo para los teclados, al fin para los modernos, tienen un navegador genérico, por lo que ni siquiera 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' solo para usuarios del teclado) y tal vez almacenar la opción con localStorage / cookies / serveride o, de lo contrario, mantener la misma opción la próxima vez cuando llega el visitante.

Ernoriel
fuente
-4

Recomiendo encarecidamente contra este enfoque. Considere la pantalla táctil, dispositivos del tamaño de una computadora de escritorio, y tiene un conjunto diferente de problemas para resolver.

Haga que su aplicación sea utilizable sin un mouse (es decir, sin vista previa de desplazamiento).

Chris Broadfoot
fuente
1
Eso es precisamente lo que estoy tratando de hacer. Estoy tratando de crear una interfaz que funcione de la mejor manera posible en ambas tabletas (sin mouse) y con un mouse, pero esas interfaces son necesariamente muy diferentes.
Jon Gjengset
Estoy de acuerdo con broady. Lo mejor es usar una detección de dispositivo (como DeviceAtlas) y seleccionar la interfaz ofrecida en el momento de la carga.
Teemu Ikonen
-4

Me gustaría recomendar un script que me ayudó:

Había leído y probado todo lo sugerido, sin resultados suficientes.

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 desktopclase) y parece bastante bueno, y para obtener touchsoporte, solo hago la verificación regular 'ontouchend' in documenty uso la información de ambas detecciones para asumir una cosa específica que necesito.

vsync
fuente
Eso es equivalente a la detección de UA, ya planteada muchas veces en este tema. No resuelve el caso de dispositivos con mouse Y táctil como Windows 8, y definitivamente no es a prueba de futuro.
Hugo Silva
Lo uso para resolver este caso EXACTO que mencionaste en la aplicación de un cliente y funciona bien. es una prueba futura, porque es mantenida por su desarrollador.
vsync
2
El guión que mencioné (computadora portátil táctil) sería identificado por su script como "Escritorio", aunque no podría estar usando un mouse. "es una prueba futura, porque es mantenida por su desarrollador". Creo que aquí se perdió completamente el punto de "prueba futura". Y si hubieras leído y probado todo como dijiste, te habrías dado cuenta de que la respuesta de Gigi ya sugiere la inhalación de UA.
Hugo Silva
-7

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. Puede hacerlo simplemente de la siguiente manera:

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

Marcus Swope
fuente
2
Realmente no sé qué obtener de su publicación, pero if ('onmouseover' en $ ('body') [0]) alert ('onmouseover'); también muestra un mensaje en iPhone
nraynaud
1
Esto solo verifica si la función mouseover está definida, lo que sería en casi todos los navegadores. No detecta si un mouse está realmente presente.
Jon Gjengset