Détecter que le navigateur n'a pas de souris et qu'il est tactile uniquement

149

Je développe une application Web (pas un site Web avec des pages de texte intéressant) avec une interface très différente pour le toucher (votre doigt cache l'écran lorsque vous cliquez) et la souris (repose fortement sur l'aperçu au survol). Comment puis-je détecter que mon utilisateur n'a pas de souris pour lui présenter la bonne interface? Je prévois de laisser un interrupteur pour les personnes ayant à la fois la souris et le toucher (comme certains ordinateurs portables).

La capacité d'événement tactile dans le navigateur ne signifie pas en fait que l'utilisateur utilise un appareil tactile (par exemple, Modernizr ne le coupe pas). Le code qui répond correctement à la question doit renvoyer false si le périphérique dispose d'une souris, true sinon. Pour les appareils avec souris et tactile, il doit renvoyer false (pas uniquement tactile)

En remarque, mon interface tactile pourrait également convenir aux appareils à clavier uniquement, c'est donc davantage le manque de souris que je cherche à détecter.

Pour rendre le besoin plus clair, voici l'API que je cherche à implémenter:

// 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
la source
Je pense que vous devez repenser votre conception si vous souhaitez qu'une application soit applicable à la fois au bureau et au mobile / tactile, mais qu'elle ait des comportements différents pour chacune. Je ne pense pas que ce que vous recherchez soit réellement possible à ce stade, car une recherche rapide sur Google pour "javascript detect mouse" montre un message modérément utile sur quirksmode.org pour détecter divers états de la souris (clics, position, etc), mais ZERO résulte de l'existence ou non de la souris.
davethegr8
24
C'est peut-être parce que Google n'a pas aidé que je l'ai posé ici.
nraynaud
2
Avez-vous essayé Document mouseenter de jquery? $ (document) .mouseenter (fonction (e) {alert ("souris");});
Parag Gajjar
4
Après avoir envisagé près d'une douzaine de pistes prometteuses pour les rejeter en quelques minutes, cette question me rend tout à fait dingue.
Jordan Grey du

Réponses:

65

Le principal problème est que vous avez les différentes classes d'appareils / cas d'utilisation suivantes:

  1. Souris et clavier (bureau)
  2. Touchez uniquement (téléphone / tablette)
  3. Souris, clavier et tactile (ordinateurs portables tactiles)
  4. Toucher et clavier (clavier Bluetooth sur tablette)
  5. Souris uniquement (utilisateur désactivé / préférence de navigation)
  6. Clavier uniquement (utilisateur désactivé / préférence de navigation)
  7. Toucher et souris (par exemple, survoler les événements du stylet Galaxy Note 2)

Ce qui est pire, c'est que l'on peut passer de certaines de ces classes à d'autres (branche une souris, se connecte au clavier), ou un utilisateur peut APPARAÎTRE être sur un ordinateur portable normal jusqu'à ce qu'il atteigne et touche l'écran.

Vous avez raison de supposer que la présence de constructeurs d'événements dans le navigateur n'est pas un bon moyen d'avancer (et c'est quelque peu incohérent). De plus, à moins que vous ne suiviez un événement très spécifique ou que vous n'essayiez d'exclure quelques classes ci-dessus, l'utilisation des événements eux-mêmes n'est pas une preuve complète.

Par exemple, supposons que vous ayez découvert qu'un utilisateur a émis un véritable mousemove (pas le faux des événements tactiles, voir http://www.html5rocks.com/en/mobile/touchandmouse/ ).

Alors quoi?

Vous activez les styles de survol? Vous ajoutez plus de boutons?

Dans tous les cas, vous augmentez le temps passé au verre parce que vous devez attendre qu'un événement se déclenche.

Mais alors que se passe-t-il lorsque votre noble utilisateur décide de débrancher sa souris et de le toucher à fond ... attendez-vous qu'il touche votre interface maintenant bourrée, puis changez-la juste après avoir fait l'effort de localiser votre interface utilisateur maintenant encombrée?

Sous forme de puce, citant stucox à https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Nous voulons détecter la présence d'une souris
  • Ae ne peut probablement pas détecter avant qu'un événement ne soit déclenché
  • En tant que tel, ce que nous détectons, c'est si une souris a été utilisée dans cette session - ce ne sera pas immédiatement à partir du chargement de la page
  • Nous ne pouvons probablement pas non plus détecter qu'il n'y a pas de souris - elle serait indéfinie jusqu'à ce qu'elle soit vraie (je pense que cela a plus de sens que de la définir sur false jusqu'à ce qu'elle soit prouvée)
  • Et nous ne pouvons probablement pas détecter si une souris est déconnectée en cours de session - ce sera impossible à distinguer de l'utilisateur abandonnant simplement sa souris

Un aparté: le navigateur sait quand un utilisateur branche une souris / se connecte à un clavier, mais ne l'expose pas à JavaScript .. dang!

Cela devrait vous conduire à ce qui suit:

Le suivi des capacités actuelles d'un utilisateur donné est complexe, peu fiable et d'un mérite douteux

L'idée de l'amélioration progressive s'applique cependant assez bien ici. Créez une expérience qui fonctionne sans problème, quel que soit le contexte de l'utilisateur. Ensuite, faites des hypothèses basées sur les fonctionnalités du navigateur / les requêtes multimédias pour ajouter des fonctionnalités qui seront relatives dans le contexte supposé. La présence d'une souris n'est que l'une des multiples façons dont différents utilisateurs sur différents appareils utilisent votre site Web. Créez quelque chose qui a du mérite au niveau de son noyau et ne vous inquiétez pas trop de la façon dont les gens cliquent sur les boutons.

Wyatt
la source
3
très bonne réponse. Espérons que l'utilisateur a toujours un écran! Je pense qu'il est logique de créer une interface qui s'adapte au mode d'interaction actuel de l'utilisateur. Sur un ordinateur portable tactile, il est logique d'ajuster l'application (c'est-à-dire les :hoveréléments et les choses comme ça) lorsque l'utilisateur passe de la souris au toucher. Il semble peu probable que l'utilisateur utilise actuellement la souris + le toucher exactement au même moment (je veux dire, c'est comme avoir 2 souris connectées au même ordinateur hahaha)
Sebastien Lorber
@SebastienLorber - je déteste vous le révéler, mais les utilisateurs n'ont pas nécessairement toujours un écran. ( Est-il possible d'utiliser javascript pour détecter si un lecteur d'écran est en cours d'exécution sur la machine d'un utilisateur? )
ashleedawg
57

Que diriez-vous d'écouter un événement mousemove sur le document. Ensuite, jusqu'à ce que vous entendiez cet événement, vous supposez que l'appareil est tactile ou clavier uniquement.

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

(Où listenet unlistendéléguer à addEventListenerou attachEventselon le cas.)

Espérons que cela ne conduirait pas à trop de jank visuel, ce serait nul si vous avez besoin de remises en page massives basées sur le mode ...

Dan
la source
2
C'est une bonne idée, mais malheureusement, le retard de réponse le rendra inutilisable lorsque l'interface utilisateur de l'application dépendra de la disponibilité d'une souris. la souris se déplace sur l'iframe lui-même ..
Jon Gjengset
7
Cela pourrait fonctionner si l'application démarre avec un écran de démarrage et un bouton "Continuer". Si la souris se déplace avant le premier événement mousedown, vous avez une souris. Cela n'échouerait que si le bouton était chargé directement sous la souris et que l'utilisateur avait une main très stable (même le déplacement d'un pixel devrait être capté).
SpliFF
49
belle idée, mais ne semble pas fonctionner dans nos tests . Les iPad déclenchent cet événement.
Jeff Atwood
3
@JeffAtwood qu'avez-vous fini par faire dans votre cas?
Michael Haren
2
Les iPad déclenchent définitivement l'événement mousemove, juste avant l'événement mousedown. J'ai trouvé que mousedown count> 0 et mousedown count == mousemove count sont un bon moyen de ne détecter aucune souris. Je ne peux pas dupliquer cela avec une vraie souris.
Peter Wooster
36

À partir de 2018, il existe un moyen efficace et fiable de détecter si un navigateur dispose d'une souris (ou d'un périphérique d'entrée similaire): les fonctionnalités d'interaction multimédia CSS4 qui sont désormais prises en charge par presque tous les navigateurs modernes (sauf IE 11 et les navigateurs mobiles spéciaux).

W3C:

La fonction multimédia de pointeur est utilisée pour interroger la présence et la précision d'un périphérique de pointage tel qu'une souris.

Voir les options suivantes:

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

Les requêtes multimédias peuvent également être utilisées dans JS:

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

En relation:

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

Prise en charge du navigateur: https://caniuse.com/#search=media%20features

Problème similaire: détecter si un périphérique client prend en charge: hover et: focus states

Blackbam
la source
Personnellement, j'aime cette réponse, mais à partir de maintenant (10/19), les requêtes CSS @media hover et pointer ne sont disponibles que sur ~ 85% des appareils dans le monde selon caniuse.com. Certainement pas mal, 95% ou plus est préférable. Espérons que cela deviendra bientôt la norme sur les appareils.
MQuiggGeorgia
1
@MQuiggGeorgia Fondamentalement, je suis d'accord avec votre critique, elle n'est pas encore supportée partout. Caniuse.com pour moi dit qu'il est pris en charge à 91,2% ( caniuse.com/#feat=css-media-interaction ). En y regardant de plus près, il est pris en charge partout, sauf pour IE 11 et les navigateurs spéciaux aplatis sur mobile. Pour être juste, cela est vrai pour toutes les fonctionnalités modernes, car Microsoft a cessé de mettre en œuvre les fonctionnalités IE depuis longtemps. Pour IE 11, vous pouvez utiliser une solution de secours par rapport aux autres réponses ici.
Blackbam
23

La réponse de @ Wyatt est excellente et nous donne beaucoup de matière à réflexion.

Dans mon cas, j'ai choisi d'écouter la première interaction, pour ensuite définir un comportement. Donc, même si l'utilisateur a une souris, je traiterai comme un appareil tactile si la première interaction était un toucher.

Compte tenu de l'ordre donné dans lequel les événements sont traités :

  1. touchstart
  2. toucher
  3. toucher
  4. survol
  5. souris
  6. souris vers le bas
  7. souris
  8. Cliquez sur

Nous pouvons supposer que si un événement de souris est déclenché avant le toucher, il s'agit d'un événement de souris réel, pas d'émulé. Exemple (en utilisant 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);
});

Cela a fonctionné pour moi

Hugo Silva
la source
Ne fonctionne pas pour moi, Ipad Safari (IOS8.3) détecte également une souris avec cet extrait de
code
3
@netzaffin. Merci pour les commentaires, j'ai trouvé que c'était plus cohérent en utilisant mousedown au lieu de mouseover. Voudriez-vous jeter un œil à ce violon depuis votre IOS et me faire part du résultat? Cheers jsfiddle.net/bkwb0qen/15/embedded/result
Hugo Silva
1
Si vous disposez d'un écran tactile avec une souris, seule la méthode de saisie utilisée en premier sera détectée.
0xcaff
11

Il est seulement possible de détecter si un navigateur est tactile capable . Il n'y a aucun moyen de savoir si un écran tactile ou une souris est connecté.

On peut cependant donner la priorité à l'utilisation en écoutant l'événement tactile au lieu de l'événement souris si la capacité tactile est détectée.

Pour détecter la fonctionnalité tactile dans plusieurs navigateurs:

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

Ensuite, on peut utiliser ceci pour vérifier:

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

ou pour choisir l'événement à écouter, soit:

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

ou utilisez des approches distinctes pour le toucher et le non-tactile:

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
}

Pour la souris, on ne peut détecter que si la souris est utilisée, pas si elle existe ou non. On peut configurer un drapeau global pour indiquer que la souris a été détectée par l'utilisation (similaire à une réponse existante, mais simplifiée un peu):

var hasMouse = false;

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

(on ne peut pas inclure mouseupou mousedowncomme ces événements peuvent également être déclenchés par le toucher)

Les navigateurs restreignent l'accès aux API système de bas niveau nécessaires pour pouvoir détecter des fonctionnalités telles que les capacités matérielles du système sur lequel il est utilisé.

Il y a la possibilité d'écrire peut-être un plugin / extension pour y accéder mais via JavaScript et DOM une telle détection est limitée à cet effet et il faudrait écrire un plugin spécifique aux différentes plates-formes OS.

Donc en conclusion: une telle détection ne peut être estimée que par une "bonne estimation".


la source
8

Lorsque Media Queries Level 4 sera disponible dans les navigateurs, nous pourrons utiliser les requêtes "pointer" et "hover" pour détecter les périphériques avec une souris.

Si nous voulons vraiment communiquer ces informations à Javascript, nous pourrions utiliser une requête CSS pour définir des styles spécifiques en fonction du type d'appareil, puis utiliser getComputedStyleen Javascript pour lire ce style et en dériver le type d'appareil d'origine.

Mais une souris peut être connectée ou débranchée à tout moment, et l'utilisateur peut souhaiter basculer entre le toucher et la souris. Il se peut donc que nous devions détecter ce changement et proposer de changer d'interface ou de le faire automatiquement.

joeytwiddle
la source
1
Plus précisément, any-pointeretany-hover vous permettra d'étudier toutes les capacités applicables de l'appareil. Ravi de voir comment nous pourrions résoudre ce problème à l'avenir! :)
Jordan Grey
2
window.matchMedia ("(any-pointer: grossier)"). correspond à === true?
4esn0k
7

Puisque vous envisagez de proposer de toute façon un moyen de basculer entre les interfaces, serait-il possible de demander simplement à l'utilisateur de cliquer sur un lien ou un bouton pour «saisir» la bonne version de l'application? Vous pourrez alors vous souvenir de leur préférence pour les prochaines visites. Ce n'est pas de la haute technologie, mais c'est 100% fiable :-)

Brandan
la source
2
C'est en fait une très bonne suggestion, mais cela retarde le temps avant que l'utilisateur n'ait accès à l'interface réelle. De plus, je devrai fournir un moyen de basculer après le choix initial. Finit par être plus de travail que s'il pouvait simplement être détecté ..
Jon Gjengset
1
Demander à l'utilisateur est clairement le meilleur moyen - sinon toujours infaillible - et vous donne un endroit pratique pour mettre en place des notifications de mise à niveau et autres. Je pense que vous avez trop réfléchi au "problème" ..
T4NK3R
4

@SamuelRossille Aucun navigateur à ma connaissance n'expose l'existence (ou l'absence de souris) d'une souris, malheureusement.

Cela étant dit, nous devons simplement essayer de faire de notre mieux avec notre option existante ... les événements. Je sais que ce n'est pas exactement ce que vous recherchez ... d'accord, il est actuellement loin d'être idéal.

Nous pouvons faire de notre mieux pour déterminer si un utilisateur utilise une souris ou une touche à un moment donné. Voici un exemple rapide et sale utilisant 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');

Vous pouvez maintenant lier / vous abonner à usingMouse () et usingTouch () et / ou styliser votre interface avec la classe body.mouse . L'interface bascule d'avant en arrière dès qu'un curseur de souris est détecté et au démarrage tactile.

J'espère que nous aurons bientôt de meilleures options de la part des fournisseurs de navigateurs.

Daredev
la source
2

Tera-WURFL peut vous indiquer les capacités de l'appareil qui visite votre site en comparant la signature du navigateur à sa base de données. Donnez-lui un coup d'oeil, c'est gratuit!

Gigi
la source
1
Cela ne fonctionnera pas pour les appareils qui peuvent ou non avoir des écrans tactiles et une souris. Par exemple, un ordinateur de bureau Windows peut être connecté à un écran tactile, mais aura généralement également une souris, alors qu'une tablette peut également exécuter Windows, mais peut ne pas avoir de souris connectée ..
Jon Gjengset
@Jonhoo Supposons simplement que les systèmes d'exploitation de bureau sont équipés d'une souris. Après tout, ils doivent prendre en charge une large gamme de logiciels qui n'ont pas été développés avec un écran tactile à l'esprit.
Gigi
1
Qu'en est-il des tablettes exécutant Windows 8? Ou Linux? Ou des ordinateurs portables fonctionnant sous Android?
Jon Gjengset
2
@Jonhoo Évidemment, cette approche est loin d'être optimale, mais il n'y a pas de moyen portable de le savoir (encore). Si vous utilisez un ordinateur portable avec Android, supposez simplement qu'il est tactile. Si vous utilisez une tablette Windows8, supposez simplement qu'elle est compatible avec la souris (le système d'exploitation doit émuler la souris pour les programmes non tactiles).
Gigi
Ceci est maintenant tellement dépassé qu'il n'est plus pertinent.
Ingénieur
2

Pourquoi ne détectez-vous pas simplement s'il a la capacité de détecter les touches et / ou de réagir aux mouvements de la souris?

// 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
la source
4
Parce que certains navigateurs (IE9 par exemple) signalent que la fonction existe même si elle ne sera jamais déclenchée. Je crois que c'est aussi le comportement «correct».
Jon Gjengset
pourquoi utiliseriez-vous une fonction? juste has_touch = 'ontouchstart' in windowsuffit, et ainsi de suite.
vsync
Eh bien, cela fonctionne sur Chrome 47 pour OS X, au moins. Signaler aucun ontouchstart.
phreakhead
2

Cela a fonctionné pour moi dans une situation similaire. Fondamentalement, supposons que l'utilisateur ne dispose pas de souris jusqu'à ce que vous voyiez une courte série de déplacements de souris consécutifs, sans intervention de descente de souris ou de souris. Pas très élégant, mais ça marche.

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
la source
0

Jetez un oeil à modenizr l'une de ses fonctionnalités est tactile

http://modernizr.com/docs/#features-misc

Bien que je n'ai pas testé complètement, cela semble fonctionner très bien

Ceci est également lié à partir de la page modernizr http://www.html5rocks.com/en/mobile/touchandmouse/

exussum
la source
Cela tente de détecter la capacité tactile, pas de savoir s'il existe UNIQUEMENT la fonction tactile et aucune capacité de souris. Cette question est fondamentalement différente de la détection tactile.
Jimbo Jonny
0

Comme d'autres l'ont souligné, détecter définitivement si oui ou non ils ont une souris n'est pas fiable. Cela peut facilement changer, en fonction de l'appareil. C'est certainement quelque chose que vous ne pouvez pas faire de manière fiable avec un booléen vrai ou faux, du moins à l'échelle d'un document.

Les événements tactiles et les événements souris sont exclusifs. Cela peut donc aider un peu à prendre différentes mesures. Le problème est que les événements tactiles sont plus proches des événements de montée / descente / déplacement de la souris et déclenchent également un événement de clic.

À partir de votre question, vous dites que vous voulez avoir un survol pour prévisualiser. Au-delà de cela, je ne connais pas d'autres détails sur votre interface. Je suppose qu'avec l'absence de souris, vous voulez qu'un clic pour prévisualiser, tandis qu'un clic effectue une action différente en raison de l'aperçu au survol.

Si tel est le cas, vous pouvez adopter une approche un peu paresseuse de la détection:

Un événement onclick sera toujours précédé d'un événement onmouseover avec une souris. Notez donc que la souris est au-dessus de l'élément sur lequel vous avez cliqué.

Vous pouvez le faire avec un événement onmousemove à l'échelle du document. Vous pouvez utiliser event.targetpour enregistrer sur quel élément se trouve la souris. Ensuite, à l'intérieur de vos événements onclick, vous pouvez vérifier si la souris est réellement sur l'élément sur lequel vous avez cliqué (ou sur un enfant de l'élément).

À partir de là, vous pouvez choisir de vous fier à l'événement de clic pour les deux et d'effectuer une action A ou B en fonction du résultat. L'action B pourrait ne rien être si certains appareils tactiles n'émettent pas d'événement de clic (à la place, vous devrez vous fier aux événements ontouch *).

Luke
la source
0

Je ne pense pas qu'il soit possible d'identifier un appareil tactile uniquement (à ma connaissance bien sûr). Le principal problème est que tous les événements de souris et de clavier sont également déclenchés par des appareils tactiles. Consultez l'exemple suivant, les deux alertes renvoient true pour les appareils tactiles.

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
la source
2
Safari iPad revient truepour'onmousedown' in window
vsync
0

La meilleure idée à mon avis est l' mousemoveauditeur (actuellement la meilleure réponse). Je pense que cette méthode doit être légèrement modifiée. Il est vrai que les navigateurs tactiles émulent même l'événement mousemove, comme vous pouvez le voir dans cette discussion iOS , nous devons donc être un peu prudents.

Il est logique que les navigateurs tactiles n'émulent cet événement que lorsque l'utilisateur appuie sur l'écran (le doigt de l'utilisateur est baissé). Cela signifie que nous devons ajouter un test lors de notre gestionnaire mousemove pour voir quel bouton de la souris est enfoncé (le cas échéant) pendant l'événement. Si aucun bouton de la souris n'est enfoncé, nous pouvons supposer qu'une vraie souris est présente. Si un bouton de la souris est enfoncé, le test n'est pas concluant.

Alors, comment cela serait-il mis en œuvre? Cette question montre que la méthode la plus fiable pour examiner quel bouton de la souris est enfoncé pendant un déplacement de la souris est en fait d'écouter 3 événements au niveau du document: mousemove, mousedown et mouseup. Le haut et le bas définiront uniquement un indicateur booléen global. Le mouvement effectuera le test. Si vous avez un mouvement et que le booléen est faux, nous pouvons supposer qu'une souris est présente. Voir la question pour des exemples de code exacts.

Un dernier commentaire. Ce test n'est pas idéal car il ne peut pas être effectué au moment du chargement. Par conséquent, j'utiliserais une méthode d'amélioration progressive comme suggéré précédemment. Par défaut, affiche une version qui ne prend pas en charge l'interface de survol spécifique à la souris. Si une souris est découverte, activez ce mode dans le runtime à l'aide de JS. Cela devrait apparaître aussi transparent que possible pour l'utilisateur.

Afin de prendre en charge les changements dans la configuration de l'utilisateur (c'est-à-dire que la souris a été déconnectée), vous pouvez périodiquement effectuer un nouveau test. Bien que je pense qu'il sera préférable dans ce cas de simplement informer l'utilisateur sur les 2 modes et de laisser les utilisateurs basculer manuellement entre eux (un peu comme le choix mobile / bureau qui peut toujours être inversé).

talkol
la source
Merci pour vos bonnes suggestions de contournement ... Je pense que le principal problème n'étant pas résolu, je vais devoir recourir à l'un de ces derniers
Samuel Rossille
Malheureusement, mousemove se déclenche en cliquant sur un iPad. Uniquement testé avec un simulateur. Pour hasMouse (), j'utilisais if (! ('Ontouchstart' in window)) return true; mais ne fonctionne pas pour les ordinateurs portables tactiles.
Chris Gunawardena
@Chris G - iPad hell ... (me cogner la tête contre le mur)
vsync
0

J'ai effectué des tests sur divers PC, Linux, iPhone, téléphones et onglets Android. Bizarre qu'il n'y ait pas de solution simple à l'épreuve des balles. Le problème survient lorsque certains qui ont Touch et pas de souris présentent toujours des événements Touch et Mouse à l'application. Étant donné que vous souhaitez prendre en charge les instances uniquement souris et tactile, vous souhaitez traiter les deux, mais cela entraîne des doubles occurrences d'interactions utilisateur. Si peut savoir que la souris n'est pas présente sur l'appareil, alors peut savoir ignorer les événements de souris faux / insérés. Indicateur de réglage essayé si MouseMove est rencontré, mais certains navigateurs lancent de faux MouseMove ainsi que MouseUp et MouseDown. J'ai essayé d'examiner les horodatages, mais j'ai pensé que c'était trop risqué. Conclusion: j'ai trouvé que les navigateurs qui ont créé les faux événements de souris inséraient toujours un seul MouseMove juste avant le MouseDown inséré. Dans 99,99% de mes cas, lors de l'exécution sur un système équipé d'une vraie souris, il y a plusieurs événements MouseMove consécutifs - au moins deux. Donc, gardez une trace de si le système rencontre deux événements MouseMove consécutifs et déclarez qu'il n'y a pas de souris présente si cette condition n'est jamais remplie. C'est probablement trop simple, mais cela fonctionne sur toutes mes configurations de test. Je pense que je m'en tiendrai jusqu'à ce que je trouve une meilleure solution. - Jim W

jwasch
la source
0

Une solution simple dans jQuery pour détecter l'utilisation de la souris, qui résout le problème où les appareils mobiles déclenchent également l'événement «mousemove». Ajoutez simplement un écouteur touchstart pour supprimer l'écouteur mousemove, afin qu'il ne se déclenche pas au toucher.

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

Bien sûr, l'appareil pourrait toujours être tactile ET souris, mais ce qui précède garantira qu'une vraie souris a été utilisée.

suncat100
la source
0

Je viens de trouver une solution que je trouve assez élégante.

// 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
la source
0

J'ai rencontré le même problème, où une seule touche était également enregistrée en tant que clic. Après avoir lu les commentaires des réponses les plus votées, j'ai trouvé ma propre solution:

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

Le seul problème que j'ai trouvé est que vous devez être capable de faire défiler pour détecter correctement un événement tactile. un seul onglet (toucher) peut poser des problèmes.

Jannis Nexor Ianus Betz
la source
0

J'ai passé des heures à résoudre ce problème pour mon application Phonegap et j'ai inventé ce hack. Il génère un avertissement de console si l'événement déclenché est un événement "passif", c'est-à-dire qu'il n'active aucun changement, mais cela fonctionne! Je serais intéressé par des idées à améliorer ou une meilleure méthode. Mais cela me permet effectivement d'utiliser $ .touch () universellement.

$(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>

Geai
la source
0

le principal problème que je vois ici est que la plupart des appareils tactiles déclenchent un événement de souris avec le touch one correspondant (touchstart -> mousedown, touchmove -> mousemove, etc.). Pour les claviers seuls, enfin pour les plus modernes, ils ont un navigateur générique donc vous ne pouvez même pas détecter la présence de la classe MouseEvent.

La solution la moins pénible ici serait, à mon avis, d'afficher un menu au lancement (avec gestion 'alt' pour les utilisateurs du clavier uniquement) et peut-être de stocker le choix avec localStorage / cookies / serverside ou bien de garder le même choix le suivant moment où le visiteur vient.

Ernoriel
la source
-4

Je déconseille fortement cette approche. Pensez aux appareils à écran tactile de la taille d'un bureau et vous avez un ensemble différent de problèmes à résoudre.

Veuillez rendre votre application utilisable sans souris (c'est-à-dire sans aperçu au survol).

Chris Broadfoot
la source
1
C'est précisément ce que j'essaie de faire. J'essaie de créer une interface qui fonctionnera de la meilleure façon possible sur les deux tablettes (pas de souris) et avec une souris, mais ces interfaces sont forcément très différentes.
Jon Gjengset
Je suis d'accord avec Broady. Il est préférable d'utiliser une détection de périphérique (comme DeviceAtlas) et de sélectionner l'interface proposée au moment du chargement.
Teemu Ikonen
-4

Je voudrais recommander un script qui m'a aidé:

J'avais lu et essayé tout ce qui était suggéré, sans résultats suffisants.

Ensuite, j'ai enquêté un peu plus et j'ai trouvé ce code - device.js

J'utilise ceci dans le site Web de mon client, pour détecter l'existence de la souris:
( <html>devrait avoir une desktopclasse) et cela semble assez bon, et pour le touchsupport, je fais juste la vérification régulière 'ontouchend' in documentet j'utilise les informations des deux détections pour supposer une chose spécifique dont j'ai besoin.

vsync
la source
Cela équivaut au sniffing UA, déjà soulevé à plusieurs reprises dans ce sujet. Cela ne résout pas le cas des appareils avec souris ET tactile comme Windows 8, et ce n'est certainement pas une preuve pour l'avenir.
Hugo Silva
Je l'utilise pour résoudre ce cas EXACT que vous avez mentionné dans l'application d'un client et cela fonctionne bien. il est à l'épreuve du temps, car il est maintenu par son développeur.
vsync
2
Le cas que j'ai mentionné (ordinateur portable tactile) serait identifié par votre script comme "Desktop", même si je n'utilise pas de souris. "il est à l'épreuve du futur, car il est maintenu par son développeur" - Je pense que vous avez complètement manqué le point de "l'épreuve du futur" ici. Et si vous aviez tout lu et tout essayé comme vous l'avez dit, vous auriez remarqué que la réponse de Gigi suggère déjà de renifler UA.
Hugo Silva
-7

Il est généralement préférable de détecter si la fonction de survol de la souris est prise en charge plutôt que de détecter le type de système d'exploitation / navigateur. Vous pouvez le faire simplement de la manière suivante:

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

Assurez-vous de ne pas effectuer les opérations suivantes:

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

Ce dernier appellerait en fait la méthode, plutôt que de vérifier son existence.

Vous pouvez en savoir plus ici: http://www.quirksmode.org/js/support.html

Marcus Swope
la source
2
Je ne sais pas vraiment quoi obtenir de votre message, mais if ('onmouseover' in $ ('body') [0]) alert ('onmouseover'); affiche également un message sur iPhone
nraynaud
1
Cela vérifie uniquement si la fonction de survol de la souris est définie, ce qu'elle serait sur presque tous les navigateurs. Il ne détecte pas si une souris est réellement présente.
Jon Gjengset