Rilevare che il browser non ha mouse ed è solo touch


149

Sto sviluppando una webapp (non un sito Web con pagine di testo interessante) con un'interfaccia molto diversa per il tocco (il dito nasconde lo schermo quando si fa clic) e il mouse (si basa fortemente sull'anteprima al passaggio del mouse). Come posso rilevare che il mio utente non ha il mouse per presentargli l'interfaccia giusta? Ho in programma di lasciare un interruttore per le persone sia con il mouse che con il tocco (come alcuni notebook).

La funzionalità dell'evento touch nel browser non significa in realtà che l'utente stia utilizzando un dispositivo touch (ad esempio, Modernizr non lo taglia). Il codice che risponde correttamente alla domanda dovrebbe restituire falso se il dispositivo ha un mouse, vero altrimenti. Per i dispositivi con mouse e touch, dovrebbe restituire false (non solo touch)

Come nota a margine, la mia interfaccia touch potrebbe anche essere adatta per dispositivi solo tastiera, quindi è più la mancanza di mouse che sto cercando di rilevare.

Per rendere più chiara la necessità, ecco l'API che sto cercando di implementare:

// 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);


Penso che tu debba ripensare il tuo design se vuoi che un'app sia applicabile sia al desktop che al mobile / touch ma abbia comportamenti diversi per ciascuno. Non penso che ciò che stai cercando sia effettivamente possibile a questo punto, poiché una rapida ricerca su Google per "Rilevamento mouse JavaScript" mostra un post moderatamente utile su quirksmode.org per rilevare vari stati del mouse (clic, posizione, ecc.), ma ZERO risulta sull'esistenza o meno del mouse.
davethegr8,

24
Forse perché Google non mi ha aiutato a chiederlo qui.
nraynaud,

2
Hai provato document mouseenter da jquery? $ (documento) .mouseenter (funzione (e) {alert ("mouse");});
Parag Gajjar,

4
Dopo aver considerato quasi una dozzina di strade promettenti solo per rifiutare ognuna in pochi minuti, questa domanda mi sta facendo impazzire magnificamente.
Jordan Gray,

Risposte:


65

Il problema principale è che hai le seguenti diverse classi di dispositivi / casi d'uso:

  1. Mouse e keyboad (desktop)
  2. Solo tocco (telefono / tablet)
  3. Mouse, tastiera e touch (touch laptop)
  4. Touch e tastiera (tastiera bluetooth sul tablet)
  5. Solo mouse (utente disabilitato / preferenza di navigazione)
  6. Solo tastiera (utente disabilitato / preferenza di navigazione)
  7. Tocco e mouse (ad es. Eventi hover dalla penna Galaxy Note 2)

Quel che è peggio, è che si può passare da alcune di queste classi ad altre (collegare un mouse, connettersi alla tastiera), oppure un utente può APPEAR di stare su un normale laptop fino a quando non si allungano e toccano lo schermo.

Hai ragione nel supporre che la presenza di costruttori di eventi nel browser non sia un buon modo per andare avanti (ed è in qualche modo incoerente). Inoltre, a meno che non si stia monitorando un evento molto specifico o si stia solo cercando di escludere alcune classi sopra, l'utilizzo degli eventi stessi non è una prova completa.

Ad esempio, supponiamo che tu abbia scoperto che un utente ha emesso un vero mousemove (non quello falso degli eventi touch, vedi http://www.html5rocks.com/en/mobile/touchandmouse/ ).

E allora?

Abiliti gli stili hover? Hai aggiunto più pulsanti?

Ad ogni modo stai aumentando il tempo per il vetro perché devi aspettare che un evento si attivi.

Ma poi cosa succede quando il tuo nobile utente decide di scollegare il mouse e passare al tocco completo. Aspetti che tocchi la tua interfaccia ora piena, quindi la cambi subito dopo che ha fatto lo sforzo di individuare la tua UI ora affollata?

In forma di proiettile, citando stucox su https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Vogliamo rilevare la presenza di un mouse
  • Probabilmente non è possibile rilevare prima che un evento venga generato
  • Pertanto, ciò che stiamo rilevando è se in questa sessione è stato utilizzato un mouse: non sarà immediatamente dal caricamento della pagina
  • Probabilmente non possiamo nemmeno rilevare che non c'è un mouse: sarebbe indefinito fino a quando non è vero (penso che questo abbia più senso che impostarlo falso fino a prova)
  • E probabilmente non siamo in grado di rilevare se un mouse è disconnesso a metà sessione - sarà indistinguibile dall'utente che si arrende con il mouse

A parte: il browser sa quando un utente collega un mouse / si collega a una tastiera, ma non lo espone a JavaScript .. dang!

Questo dovrebbe portare a quanto segue:

Tracciare le attuali capacità di un determinato utente è complesso, inaffidabile e di dubbia utilità

L'idea del miglioramento progressivo si applica abbastanza bene qui, però. Crea un'esperienza che funzioni senza problemi, indipendentemente dal contesto dell'utente. Quindi fare ipotesi basate sulle funzionalità del browser / query multimediali per aggiungere funzionalità che saranno relative nel contesto presunto. La presenza di un mouse è solo uno dei tanti modi in cui diversi utenti su dispositivi diversi sperimentano il tuo sito Web. Crea qualcosa con merito nel suo kernel e non preoccuparti troppo di come le persone fanno clic sui pulsanti.


3
Bella risposta. Speriamo che l'utente abbia sempre uno schermo! Penso che abbia senso costruire un'interfaccia in cui sia sufficiente l'attuale modalità di interazione dell'utente. Su un laptop touch, ha senso regolare l'app (cioè gli :hoverelementi e cose del genere) quando l'utente passa dal mouse al tocco. Sembra improbabile che l'utente stia attualmente usando il mouse + touch nello stesso momento (intendo comoonn è come avere 2 mouse collegati allo stesso computer hahaha)
Sebastien Lorber

@SebastienLorber - odio interromperti, ma gli utenti non necessariamente hanno sempre uno schermo. ( È possibile usare javascript per rilevare se uno screen reader è in esecuzione su una macchina degli utenti? )
ashleedawg

57

Che ne dite di ascoltare un evento mousemove sul documento. Quindi fino a quando non senti quell'evento supponi che il dispositivo sia solo touch o tastiera.

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

(Dove listene unlistendelegare addEventListenero attachEventcome appropriato.)

Spero che questo non porti a troppi jank visivi, farebbe schifo se avessi bisogno di grandi ri-layout basati sulla modalità ...


2
È una buona idea, ma sfortunatamente il ritardo nella risposta lo renderà inutilizzabile quando l'interfaccia utente dell'applicazione dipende dalla disponibilità di un mouse. Ciò è particolarmente vero se l'applicazione può essere incorniciata, quindi gli eventi del mouse la colpiranno solo se il il mouse si sposta
sull'iframe

7
Questo potrebbe funzionare se l'applicazione inizia con una schermata iniziale e un pulsante "continua". Se il mouse si sposta prima del primo evento di mouse, allora hai un mouse. Avrebbe esito negativo solo se il pulsante fosse caricato direttamente sotto il mouse e l'utente avesse una mano molto stabile (anche se si sposta 1 pixel dovrebbe essere raccolto).
SpliFF,

49
bella idea, ma non sembra funzionare nei nostri test . Gli iPad attivano questo evento.
Jeff Atwood,

3
@JeffAtwood cosa hai fatto nel tuo caso?
Michael Haren,

2
Gli iPad innescano sicuramente l'evento mousemove, proprio prima dell'evento mousedown. Ho scoperto che il conteggio del mouse> 0 e il conteggio del mouse == il conteggio del mouse sono un buon modo per rilevare nessun mouse. Non posso duplicarlo con un mouse reale.
Peter Wooster,

36

A partire dal 2018 esiste un modo valido e affidabile per rilevare se un browser ha un mouse (o un dispositivo di input simile): funzionalità di interazione multimediale CSS4 che sono ora supportate da quasi tutti i browser moderni (tranne IE 11 e speciali browser mobili).

W3C:

La funzione di puntatore multimediale viene utilizzata per interrogare la presenza e l'accuratezza di un dispositivo di puntamento come un mouse.

Vedi le seguenti opzioni:

    /* 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) { ... }

Le query multimediali possono essere utilizzate anche in JS:

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

Relazionato:

Documentazione W3: https://www.w3.org/TR/mediaqueries-4/#mf-interaction

Supporto per browser: https://caniuse.com/#search=media%20features

Problema simile: Rileva se un dispositivo client supporta: hover e: focus stati


Personalmente mi piace questa risposta ma a partire da ora (10/10), le query CSS di @media hover e pointer sono disponibili solo su ~ 85% dei dispositivi in ​​tutto il mondo secondo caniuse.com. Certamente non male, è preferibile il 95% o più. Speriamo che questo diventi presto standard sui dispositivi.
MQuiggGeorgia,

1
@MQuiggGeorgia Concordo sostanzialmente con le tue critiche, non è ancora supportato ovunque. Ancora caniuse.com per me dice che è supportato al 91,2% ( caniuse.com/#feat=css-media-interaction ). Dando un'occhiata più da vicino, è supportato ovunque tranne IE 11 e speciali browser appiattiti sui dispositivi mobili. Ad essere sinceri, questo vale per qualsiasi funzionalità moderna, poiché Microsoft ha smesso di implementare le funzionalità di IE molto tempo fa. Per IE 11 potresti utilizzare un fallback dalle altre risposte qui.
Blackbam,

23

La risposta di Wyatt è fantastica e ci dà molto su cui riflettere.

Nel mio caso, ho scelto di ascoltare la prima interazione, solo per stabilire un comportamento. Quindi, anche se l'utente ha un mouse, tratterò come dispositivo touch se la prima interazione fosse un tocco.

Considerando l' ordine dato in cui gli eventi vengono elaborati :

  1. TouchStart
  2. touchmove
  3. touchend
  4. mouseover
  5. MouseMove
  6. mousedown
  7. MouseUp
  8. clic

Possiamo supporre che se l'evento del mouse viene attivato prima del tocco, è un evento del mouse reale, non emulato. Esempio (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);
});

Questo ha funzionato per me


Non funziona per me, Ipad Safari (IOS8.3) rileva anche un mouse con questo frammento
netzaffin

3
@netzaffin. Grazie per il feedback, l'ho trovato più coerente usando mousedown invece di mouseover. Vuoi dare un'occhiata a questo violino dal tuo IOS e farmi sapere il risultato? Cin cin jsfiddle.net/bkwb0qen/15/embedded/result
Hugo Silva

1
Se si dispone di un touchscreen con il mouse, verrà rilevato solo il metodo di input utilizzato per primo.
0xcaff,

11

È possibile rilevare solo se un browser è abilitato al tocco . Non c'è modo di sapere se in realtà ha un touchscreen o un mouse collegato.

È possibile stabilire la priorità dell'uso ascoltando l'evento touch anziché l'evento del mouse se viene rilevata la funzionalità touch.

Per rilevare la funzionalità touch tra browser:

function hasTouch() {
    return (('ontouchstart' in window) ||       // html5 browsers
            (navigator.maxTouchPoints > 0) ||   // future IE
            (navigator.msMaxTouchPoints > 0));  // current IE10
}

Quindi si può usare questo per verificare:

if (!hasTouch()) alert('Sorry, need touch!);

o per scegliere quale evento ascoltare, sia:

var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);

o usa approcci separati per touch e non-touch:

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
}

Per il mouse si può rilevare solo se il mouse è in uso, non se esiste o no. Si può impostare un flag globale per indicare che il mouse è stato rilevato dall'uso (simile a una risposta esistente, ma semplificato un po '):

var hasMouse = false;

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

(non è possibile includere mouseupo mousedownpoiché questi eventi possono essere attivati ​​anche al tocco)

I browser limitano l'accesso alle API di sistema di basso livello necessarie per poter rilevare funzionalità come le capacità hardware del sistema su cui vengono utilizzate.

C'è forse la possibilità di scrivere un plugin / estensione per accedervi ma tramite JavaScript e DOM tale rilevazione è limitata a questo scopo e si dovrebbe scrivere un plugin specifico per le varie piattaforme del sistema operativo.

Quindi in conclusione: tale rilevazione può essere stimata solo da una "buona ipotesi".


8

Quando Media Queries Level 4 è disponibile nei browser, saremo in grado di utilizzare le query "pointer" e "hover" per rilevare i dispositivi con un mouse.

Se vogliamo davvero comunicare tali informazioni a Javascript, potremmo utilizzare una query CSS per impostare stili specifici in base al tipo di dispositivo, quindi utilizzare getComputedStylein Javascript per leggere quello stile e ricavarne il tipo di dispositivo originale.

Ma un mouse può essere collegato o scollegato in qualsiasi momento e l'utente potrebbe voler passare dal tocco al mouse. Quindi potremmo aver bisogno di rilevare questo cambiamento e offrirci di cambiare interfaccia o farlo automaticamente.


1
In particolare, any-pointereany-hover ti consentirà di esaminare tutte le funzionalità del dispositivo applicabili. Bello dare un'occhiata a come potremmo risolvere questo problema in futuro! :)
Jordan Grey,

2
window.matchMedia ("(any-pointer: grossolana)"). match === vero?
4esn0k,

7

Dal momento che hai intenzione di offrire un modo per passare tra le interfacce comunque, sarebbe possibile semplicemente chiedere all'utente di fare clic su un link o un pulsante per "inserire" la versione corretta dell'applicazione? Quindi potresti ricordare la loro preferenza per le visite future. Non è high-tech, ma è affidabile al 100% :-)


2
In realtà è un suggerimento abbastanza buono, ma ritarda il tempo prima che l'utente arrivi all'interfaccia reale. Inoltre, dovrò fornire un modo per passare dopo la scelta iniziale. Finisce per essere più lavoro che se potesse essere semplicemente rilevato ..
Jon Gjengset

1
Chiedere all'utente è chiaramente il modo migliore - se non sempre infallibile - E ti dà un posto conveniente per inviare notifiche di aggiornamento e cosa no. Penso che tu stia pensando troppo al "problema" ..
T4NK3R

4

@SamuelRossille Nessun browser di cui sono a conoscenza svela l'esistenza di un mouse (o la sua mancanza), sfortunatamente.

Quindi, detto questo, non ci resta che provare e fare il meglio che possiamo con la nostra opzione esistente ... eventi. So che non è esattamente quello che stai cercando ... concordato che attualmente è tutt'altro che ideale.

Possiamo fare del nostro meglio per capire se un utente sta utilizzando un mouse o un tocco in un dato momento. Ecco un esempio veloce e sporco 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');

Ora puoi associare / iscriverti a usingMouse () & usingTouch () e / o ad applicare la tua interfaccia alla classe body.mouse . L'interfaccia passerà avanti e indietro non appena viene rilevato un cursore del mouse e al touchstart.

Speriamo che presto avremo alcune opzioni migliori dai venditori di browser.


2

Tera-WURFL può dirti le capacità del dispositivo che sta visitando il tuo sito confrontando la firma del browser con il suo database. Dai un'occhiata, è gratis!


1
Questo non funzionerà per i dispositivi che possono o meno avere touchscreen e mouse. Ad esempio, un computer desktop Windows potrebbe essere collegato a un touchscreen, ma di solito avrà anche un mouse, mentre un tablet potrebbe anche eseguire Windows, ma potrebbe non avere un mouse collegato.
Jon Gjengset,

@Jonhoo Supponiamo solo che i sistemi operativi desktop abbiano un mouse collegato. Dopotutto, devono supportare una vasta gamma di software che non è stato sviluppato pensando a un touchscreen.
Gigi,

1
Che dire dei tablet con Windows 8 normale? O Linux? O laptop con Android?
Jon Gjengset,

2
@Jonhoo Ovviamente questo approccio è tutt'altro che ottimale, ma non esiste un modo portatile per saperlo (ancora). Se uno esegue un laptop con Android, supponiamo che sia compatibile con il tocco. Se si esegue un tablet Windows8, supponiamo che sia compatibile con il mouse (il sistema operativo deve emulare il mouse per i programmi non touch).
Gigi,

Questo è ora così obsoleto che non è più rilevante.
Ingegnere

2

Perché non rilevi solo se ha la capacità di rilevare i tocchi e / o reagire ai movimenti 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);
}

4
Perché alcuni browser (ad esempio IE9) segnalano che la funzione esiste anche se non verrà mai attivata. Credo che questo sia anche il comportamento "corretto".
Jon Gjengset,

perché dovresti usare una funzione? appena has_touch = 'ontouchstart' in windowsarà sufficiente, e così via.
vsync,

Bene, funziona almeno su Chrome 47 per OS X. Segnalazione no ontouchstart.
phreakhead

2

Questo ha funzionato per me in una situazione simile. Fondamentalmente, supponi che l'utente non abbia un mouse fino a quando non vedi una breve serie di tappi per mouse consecutivi, senza intervenire tra mouse e mouse. Non molto elegante, ma funziona.

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


0

Come altri hanno sottolineato, rilevare in modo definitivo se hanno o meno un mouse non è affidabile. Questo può facilmente cambiare, a seconda del dispositivo. È sicuramente qualcosa che non puoi fare in modo affidabile con un vero o falso booleano, almeno su una scala di documenti.

Gli eventi touch e gli eventi del mouse sono esclusivi. Quindi questo può aiutare in qualche modo ad intraprendere azioni diverse. Il problema è che gli eventi touch sono più vicini agli eventi su / giù / sposta del mouse e attivano anche un evento clic.

Dalla tua domanda dici che vuoi avere un passaggio del mouse per visualizzare l'anteprima. Oltre a ciò, non conosco altri dettagli sulla tua interfaccia. Sto assumendo che con la mancanza di un mouse che si desidera un rubinetto di anteprima, mentre un click fa un'azione diversa a causa della preview hover.

In tal caso, puoi adottare un approccio un po 'pigro al rilevamento:

Un evento onclick sarà sempre preceduto da un evento onmouseover con il mouse. Quindi prendi nota che il mouse si trova sopra l'elemento su cui è stato fatto clic.

Potresti farlo con un evento onmousemove a livello di documento. Puoi usare event.targetper registrare su quale elemento si trova il mouse. Quindi, all'interno degli eventi onclick, puoi verificare se il mouse si trova effettivamente sull'elemento su cui fai clic (o su un elemento figlio dell'elemento).

Da lì puoi scegliere di fare affidamento sull'evento click per entrambi e compiere un'azione A o B a seconda del risultato. L'azione B potrebbe non essere nulla se alcuni dispositivi touch non emettono un evento click (invece dovresti fare affidamento su eventi intouch *).


0

Non penso che sia possibile identificare il dispositivo solo touch (per quanto ne so ovviamente). Il problema principale è che tutti gli eventi del mouse e della tastiera vengono attivati ​​anche dai dispositivi touch. Vedi l'esempio seguente, entrambi gli avvisi diventano veri per i dispositivi touch.

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());

2
Safari ipad ritorna trueper'onmousedown' in window
vsync il

0

L'idea migliore secondo me è l' mousemoveascoltatore (attualmente la risposta migliore). Credo che questo metodo debba essere leggermente modificato. È vero che i browser touch-based emulano anche l'evento mousemove, come puoi vedere in questa discussione su iOS , quindi dovremmo stare un po 'attenti.

È logico che i browser basati su touch emulino questo evento solo quando l'utente tocca lo schermo (il dito dell'utente è abbassato). Ciò significa che dovremmo aggiungere un test durante il nostro gestore mousemove per vedere quale pulsante del mouse è premuto (se presente) durante l'evento. Se nessun pulsante del mouse è premuto, possiamo tranquillamente presumere che sia presente un mouse reale. Se un pulsante del mouse è premuto, il test rimane inconcludente.

Quindi come verrebbe implementato? Questa domanda mostra che il metodo più affidabile per esaminare quale pulsante del mouse è premuto durante un mousemove è effettivamente ascoltare 3 eventi a livello di documento: mousemove, mousedown e mouseup. Su e giù imposteranno solo una bandiera booleana globale. La mossa eseguirà il test. Se hai una mossa e il valore booleano è falso, possiamo presumere che sia presente un mouse. Vedi la domanda per esempi di codice esatti.

Un commento finale .. Questo test non è l'ideale perché non può essere eseguito in tempo di caricamento. Pertanto, utilizzerei un metodo di miglioramento progressivo come precedentemente suggerito. Per impostazione predefinita, mostra una versione che non supporta l'interfaccia hover specifica del mouse. Se viene rilevato un mouse, abilitare questa modalità in fase di esecuzione utilizzando JS. Questo dovrebbe apparire il più semplice possibile per l'utente.

Per supportare le modifiche nella configurazione dell'utente (ovvero il mouse è stato disconnesso), è possibile ripetere periodicamente il test. Anche se credo che sarà meglio in questo caso informare semplicemente l'utente delle 2 modalità e consentire agli utenti di passare manualmente da una all'altra (proprio come la scelta mobile / desktop che può sempre essere invertita).


Grazie per i buoni suggerimenti per risolvere il problema ... Penso che il problema principale non essendo il soviet dovrò ricorrere a uno di questi
Samuel Rossille,

Purtroppo mousemove viene attivato facendo clic su un iPad. Testato solo con simulatore. Per hasMouse () stavo usando if (! ('Ontouchstart' nella finestra)) return true; ma non funziona per i computer portatili con supporto touch.
Chris Gunawardena,

@Chris G - Inferno iPad ... (sbattendo la testa contro il muro)
vsync

0

Ho eseguito alcuni test su vari PC e schede Linux, iPhone, telefoni Android. Strano che non ci sia una soluzione semplice a prova di proiettile. Il problema sorge quando alcuni che hanno Touch e nessun mouse presentano ancora eventi Touch e Mouse all'applicazione. Dal momento che vogliono supportare istanze solo mouse e solo tocco, vogliono elaborare entrambi, ma questo provoca doppie occorrenze di interazioni dell'utente. Se sai che il mouse non è presente sul dispositivo, puoi sapere di ignorare gli eventi del mouse falsi / inseriti. Provato flag di impostazione se si incontra MouseMove, ma alcuni browser generano MouseMove falso, nonché MouseUp e MouseDown. Ho provato a esaminare i timestamp ma ho pensato che fosse troppo rischioso. In conclusione: ho scoperto che i browser che hanno creato gli eventi del mouse falso hanno sempre inserito un singolo MouseMove appena prima del MouseDown inserito. Nel 99,99% dei miei casi, quando si esegue su un sistema che ha un mouse reale, ci sono più eventi consecutivi MouseMove - almeno due. Quindi, controlla se il sistema rileva due eventi MouseMove consecutivi e dichiara che non è presente mouse se questa condizione non viene mai soddisfatta. Questo è probabilmente troppo semplice, ma funziona su tutte le mie configurazioni di test. Penso che rimarrò con esso fino a quando non troverò una soluzione migliore. - Jim W


0

Una soluzione semplice in jQuery per rilevare l'utilizzo del mouse, che risolve il problema per cui i dispositivi mobili attivano anche l'evento "mousemove". Aggiungi semplicemente un listener touchstart per rimuovere il listener mousemove, in modo che non venga attivato al tocco.

$('body').one('touchstart.test', function(e) {
  // Remove the mousemove listener for touch devices
  $('body').off('mousemove.test');
}).one('mousemove.test', function(e) {
  // MOUSE!
});

Ovviamente, il dispositivo potrebbe essere ancora touch E mouse, ma quanto sopra garantirà che è stato utilizzato un mouse reale.


0

Ho appena trovato una soluzione che penso sia abbastanza 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
    // ...
  }
});

0

Ho riscontrato lo stesso problema, in cui è stato registrato anche un solo tocco come clic. Dopo aver letto i commenti delle risposte più votate, ho trovato la mia soluzione:

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

L'unico problema che ho riscontrato è che devi essere in grado di scorrere, per rilevare correttamente un evento touch. una singola scheda (tocco) potrebbe creare problemi.


0

Ho trascorso ore a capire questo problema per la mia app Phonegap e ho trovato questo trucco. Genera un avviso di console se l'evento attivato è un evento "passivo", il che significa che non attua alcun cambiamento, ma funziona! Sarei interessato a qualsiasi idea per migliorare o un metodo migliore. Ma questo mi permette di usare $ .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>


0

il problema principale che vedo qui è che la maggior parte dei dispositivi touch genera un evento del mouse insieme a quello corrispondente (touchstart -> mousedown, touchmove -> mousemove, ecc.). Solo per la tastiera, finalmente per quelli moderni, hanno un browser generico quindi non puoi nemmeno rilevare la presenza della classe MouseEvent.

La soluzione meno dolorosa qui sarebbe, secondo me, visualizzare un menu all'avvio (con gestione 'alt' per utenti solo tastiera) e magari memorizzare la scelta con LocalStorage / cookies / serveride oppure mantenere la stessa scelta la prossima quando arriva il visitatore.


-4

Consiglio vivamente contro questo approccio. Prendi in considerazione dispositivi touch-screen di dimensioni desktop e hai una serie diversa di problemi da risolvere.

Rendi utilizzabile la tua app senza mouse (ovvero senza anteprima al passaggio del mouse).


1
Questo è esattamente quello che sto cercando di fare. Sto cercando di creare un'interfaccia che funzionerà nel modo migliore su entrambi i tablet (senza mouse) e con un mouse, ma quelle interfacce sono necessariamente molto diverse.
Jon Gjengset,

Sono d'accordo con broady. È meglio utilizzare un rilevamento del dispositivo (come DeviceAtlas) e selezionare l'interfaccia offerta al momento del caricamento.
Teemu Ikonen,

-4

Vorrei raccomandare una sceneggiatura che mi ha aiutato:

Avevo letto e provato tutto suggerito, senza risultati sufficienti.

Poi ho studiato di più e ho trovato questo codice - device.js

Sto usando questo nel sito Web del mio cliente, per rilevare l'esistenza del mouse:
( <html>dovrebbe avere desktopclasse) e sembra abbastanza buono, e per touchsupporto, faccio semplicemente il controllo regolare 'ontouchend' in documente uso le informazioni di entrambi i rilevamenti per assumere una cosa specifica di cui ho bisogno.


Ciò equivale a sniffare UA, già sollevato molte volte in questo argomento. Non risolve il caso dei dispositivi con mouse E tocco come Windows 8, e sicuramente non è a prova di futuro.
Hugo Silva,

Lo uso per risolvere questo caso EXACT che hai citato nell'applicazione di un client e funziona bene. è a prova di futuro, perché è gestito dal suo sviluppatore.
vsync,

2
Il caso che ho citato (laptop abilitato al tocco) sarebbe identificato dal tuo script come "Desktop", anche se non potrei usare un mouse. "è una prova futura, perché è gestita dal suo sviluppatore" - Penso che tu abbia completamente perso il punto di "prova futura" qui. E se avessi letto e provato tutto come hai affermato, avresti notato che la risposta di Gigi suggerisce già che UA annusa.
Hugo Silva,

-7

In genere è un'idea migliore rilevare se la funzione mouseover è supportata anziché rilevare il tipo di sistema operativo / browser. Puoi farlo semplicemente come segue:

if (element.mouseover) {
    //Supports the mouseover event
}

Assicurati di non eseguire le seguenti operazioni:

if (element.mouseover()) {
    // Supports the mouseover event
}

Quest'ultimo chiamerebbe effettivamente il metodo, piuttosto che verificarne l'esistenza.

Puoi leggere di più qui: http://www.quirksmode.org/js/support.html


2
Non so davvero cosa ottenere dal tuo post, ma if ('onmouseover' in $ ('body') [0]) alert ('onmouseover'); visualizza un messaggio anche su iPhone
nraynaud,

1
Questo controlla solo se è definita la funzione mouseover, che sarebbe su quasi tutti i browser. Non rileva se un mouse è effettivamente presente.
Jon Gjengset
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.