124 votes

Désactiver les effets de survol sur les navigateurs mobiles

J'écris un site Web qui doit être utilisé à la fois depuis des ordinateurs de bureau et des tablettes. Lorsqu'il est visité à partir d'un ordinateur de bureau, je veux que les zones cliquables de l'écran s'illuminent avec :hover Avec une tablette, il n'y a pas de souris, donc je ne veux pas d'effets de survol.

Le problème est que, lorsque je touche quelque chose sur la tablette, le navigateur dispose manifestement d'une sorte de "curseur de souris invisible" qu'il déplace à l'endroit où j'ai touché, puis le laisse là - de sorte que la chose que je viens de toucher s'allume avec un effet de survol jusqu'à ce que je touche quelque chose d'autre.

Comment puis-je obtenir les effets de survol lorsque j'utilise la souris, mais les supprimer lorsque j'utilise l'écran tactile ?

Au cas où quelqu'un aurait l'intention de le suggérer, je ne veux pas utiliser le reniflage des user-agents. Le même appareil pourrait être équipé d'un écran tactile et d'une souris (ce qui n'est peut-être pas si courant aujourd'hui, mais le sera beaucoup plus à l'avenir). Je ne m'intéresse pas à l'appareil, mais à la façon dont il est utilisé : souris ou écran tactile.

J'ai déjà essayé d'accrocher le touchstart , touchmove et touchend et appeler preventDefault() sur chacun d'entre eux, ce qui supprime le "curseur invisible de la souris" de temps en temps ; mais si je tape rapidement d'avant en arrière entre deux éléments différents, après quelques tapotements, le "curseur de la souris" commencera à se déplacer et les effets de survol s'allumeront de toute façon -- c'est comme si mon preventDefault n'est pas toujours honorée. Je ne vous ennuierai pas avec les détails, sauf si cela est nécessaire - je ne suis même pas sûr que ce soit la bonne approche à adopter ; si quelqu'un a une solution plus simple, je suis tout ouïe.


Edit : Ceci peut être reproduit avec un CSS standard. :hover mais voici une reproduction rapide pour référence.

<style>
  .box { border: 1px solid black; width: 150px; height: 150px; }
  .box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>

Si vous passez la souris sur l'une ou l'autre des cases, elle aura un fond bleu, ce que je veux. Mais si vous tapez sur l'une des cases, l'arrière-plan sera également bleu, ce que j'essaie d'éviter.

J'ai également posté un échantillon ici qui fait ce qui précède et accroche également les événements de souris de jQuery. Vous pouvez l'utiliser pour voir que les événements de tapotement se déclenchent également. mouseenter , mousemove et mouseleave .

7 votes

@Blender, avez-vous lu la question ? J'ai déjà expliqué pourquoi le reniflage de l'agent utilisateur est un mauvais choix.

0 votes

Hey Joe, avez-vous trouvé une solution à ce problème ?

0 votes

Je cherchais ce genre de question, je suis content de l'avoir trouvé !

89voto

MacFreek Points 391

Je déduis de votre question que votre effet de survol modifie le contenu de votre page. Dans ce cas, mon conseil est de :

  • Ajouter des effets de survol sur touchstart et mouseenter .
  • Suppression des effets de survol sur mouseleave , touchmove et click .

Vous pouvez également modifier votre page pour qu'il n'y ait pas de changement de contenu.

Contexte

Afin de simuler une souris, les navigateurs tels que Webkit mobile déclenchent les événements suivants si un utilisateur touche et relâche un doigt sur un écran tactile (comme l'iPad) (source : Toucher et souris sur html5rocks.com) :

  1. touchstart
  2. touchmove
  3. touchend
  4. un délai de 300 ms, pendant lequel le navigateur s'assure qu'il s'agit d'une simple pression et non d'une double pression.
  5. mouseover
  6. mouseenter
    • Note : Si un mouseover , mouseenter ou mousemove modifie le contenu de la page, les événements suivants ne sont jamais déclenchés.
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

Il ne semble pas possible de dire simplement au navigateur web d'ignorer les événements de la souris.

Pire encore, si un événement mouseover modifie le contenu de la page, l'événement click n'est jamais déclenché, comme expliqué sur Guide du contenu Web de Safari - Traitement des événements en particulier la figure 6.4 dans Événements à un doigt . Ce qu'est exactement un "changement de contenu" dépend du navigateur et de la version. J'ai constaté que pour iOS 7.0, un changement de couleur de fond n'est pas (ou plus ?) un changement de contenu.

La solution expliquée

Pour récapituler :

  • Ajouter des effets de survol sur touchstart et mouseenter .
  • Suppression des effets de survol sur mouseleave , touchmove et click .

Notez qu'il n'y a pas d'action sur touchend !

Cela fonctionne clairement pour les événements de la souris : mouseenter et mouseleave (versions légèrement améliorées de mouseover et mouseout ) sont déclenchés, et ajoutent et suppriment le survol.

Si l'utilisateur click s un lien, l'effet de survol est également supprimé. Cela permet de s'assurer qu'il est supprimé si l'utilisateur appuie sur le bouton "retour" dans le navigateur Web.

Cela fonctionne également pour les événements tactiles : au début de la touche, l'effet de survol est ajouté. Il n'est ''pas'' supprimé à la fin du toucher. Il est ajouté à nouveau lors de mouseenter et puisque cela n'entraîne aucun changement de contenu (il était déjà ajouté), la fonction click est également déclenché, et le lien est suivi sans que l'utilisateur ait besoin de cliquer à nouveau !

Le délai de 300ms qu'un navigateur a entre un touchstart et click est en fait utilisé à bon escient car l'effet de survol sera affiché pendant ce court laps de temps.

Si l'utilisateur décide d'annuler le clic, un mouvement du doigt le fera comme d'habitude. Normalement, c'est un problème car aucun mouseleave est déclenché, et l'effet de survol reste en place. Heureusement, ce problème peut être facilement corrigé en supprimant l'effet de survol sur les éléments suivants touchmove .

C'est ça !

Notez qu'il est possible de supprimer le délai de 300 ms, par exemple en utilisant la fonction Bibliothèque FastClick mais cela n'entre pas dans le cadre de cette question.

Solutions alternatives

J'ai trouvé les problèmes suivants avec les alternatives suivantes :

  • détection du navigateur : Extrêmement sujettes aux erreurs. Suppose qu'un appareil soit équipé d'une souris ou d'un écran tactile, alors qu'une combinaison des deux deviendra de plus en plus courante lorsque les écrans tactiles se multiplieront.
  • Détection des médias en CSS : La seule solution uniquement en CSS que je connaisse. Elle est toujours sujette à des erreurs, et suppose toujours qu'un appareil est équipé d'une souris ou d'un écran tactile, alors que les deux sont possibles.
  • Emulez l'événement de clic dans touchend : Le lien sera ainsi suivi de manière incorrecte, même si l'utilisateur voulait seulement faire défiler ou zoomer, sans avoir l'intention de cliquer sur le lien.
  • Utilisez une variable pour supprimer les événements de la souris : Cela définit une variable dans touchend qui est utilisée comme condition si dans les événements de souris suivants pour empêcher les changements d'état à ce moment-là. La variable est réinitialisée dans l'événement de clic. Voir la réponse de Walter Roman sur cette page. C'est une solution décente si vous ne voulez vraiment pas d'effet de survol sur les interfaces tactiles. Malheureusement, cela ne fonctionne pas si un touchend est déclenché pour une autre raison et qu'aucun événement de clic n'est déclenché (par exemple, l'utilisateur a fait défiler ou a zoomé), et qu'il essaie ensuite de suivre le lien avec une souris (c'est-à-dire sur un appareil doté à la fois d'une souris et d'une interface tactile).

Autres lectures

1 votes

Belle réponse. J'ai utilisé votre bricolage pour commencer à créer ma propre solution. Le touchend est cependant parfois nécessaire. (quand un touchmove ne s'exécute pas -- c'est-à-dire quand l'utilisateur se déplace hors du document lui-même...)

0 votes

Excellente réponse mais dans la plupart des cas, je suggérerais d'utiliser touchend au lieu de de touchstart dans la plupart des cas, afin d'éviter de déclencher des actions immédiatement lorsque l'utilisateur essaie de faire défiler une page vers le haut ou vers le bas. Cela se produira quand même, mais risque moins de distraire ou d'embrouiller l'utilisateur (en supposant qu'il s'agit d'une action inoffensive, comme l'affichage d'une étiquette, et non d'une action dont l'utilisateur doit être informé).

19voto

nnnnnn Points 70578

Comment puis-je obtenir les effets de survol lorsque j'utilise la souris, mais les supprimer lorsque j'utilise l'écran tactile ?

Il ne s'agit peut-être pas tant de supprimer les effets de survol pour les écrans tactiles que d'ajouter des effets de survol pour les événements de la souris ?

Si vous voulez garder le :hover Dans vos CSS, vous pouvez spécifier des styles différents pour des supports différents :

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

Sauf que, malheureusement, de nombreux appareils mobiles ignorent cette règle et se contentent d'utiliser les règles de l'écran. Heureusement, un grand nombre de navigateurs récents pour mobiles/tablettes prennent en charge des requêtes média plus sophistiquées :

@media screen and (max-width:800px) { /* non-hover styles here */ }

Ainsi, même si la partie "screen" ou "handheld" est ignorée, "max-width" fera l'affaire pour vous. Vous pourriez simplement supposer que tout ce dont l'écran est inférieur à 800 pixels est une tablette ou un téléphone, et ne pas utiliser les effets de survol. Les rares utilisateurs qui se servent d'une souris sur un appareil à faible résolution ne verront pas les effets de survol, mais votre site sera parfait dans les autres cas.

Autres lectures sur les requêtes média ? Il existe de nombreux articles sur le sujet en ligne - en voici un : http://www.alistapart.com/articles/return-of-the-mobile-stylesheet

Si vous retirez les effets de survol de votre CSS et que vous les appliquez avec JavaScript, vous pouvez alors vous lier spécifiquement aux événements de la souris, et/ou encore une fois, vous pouvez simplement faire des suppositions sur la base de la taille de l'écran, le "problème" le plus grave étant qu'un utilisateur qui utilise une souris ne bénéficie pas des effets de survol.

0 votes

J'aimerais ajouter des effets de survol pour les événements de souris uniquement. Mais les événements tactiles émuler événements de la souris. Si j'accroche les événements tactiles et appelle preventDefault() qui supprime parfois les événements de la souris, mais comme je l'ai dit dans ma question, ce n'est pas fiable.

6 votes

En ce qui concerne les requêtes média, que se passera-t-il lorsque Windows 8 sortira et que les PC seront équipés d'écrans tactiles ? et des souris ? Si l'utilisateur survole l'écran avec la souris, il verra le curseur de la souris et je veux les effets de survol ; s'il touche l'écran tactile, je ne veux pas les effets de survol. Mais je n'ai pas trouvé de moyen fiable de détecter la différence entre le toucher et la souris.

0 votes

Peut-être que les développeurs de navigateurs seront plus motivés pour rendre la détection plus cohérente une fois qu'ils auront un plus grand nombre d'utilisateurs sur des appareils avec écran tactile et souris. Mais pour l'instant ? Je ferais des suppositions basées sur la taille de l'écran, mais il y a d'autres conseils dans les autres réponses. Désolé de ne pas pouvoir vous aider davantage.

10voto

Walter Roman Points 596

J'ai écrit le JS suivant pour un projet récent, qui était un site desktop/mobile/tablette qui a des effets de survol qui ne devraient pas apparaître au toucher.

Le site mobileNoHoverState Le module ci-dessous possède une variable preventMouseover (initialement déclaré comme false ), qui est réglé sur true lorsqu'un utilisateur déclenche la touchstart sur un élément, $target .

preventMouseover est ensuite ramené à false chaque fois que le mouseover est déclenché, ce qui permet au site de fonctionner comme prévu si un utilisateur utilise à la fois son écran tactile et sa souris.

Nous savons que mouseover est déclenché après touchstart en raison de l'ordre dans lequel ils sont déclarés au sein de l'UE. init .

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

Le module est ensuite instancié plus loin dans la ligne :

mobileNoHoverState.init();

0 votes

"preventMouseover est ensuite remis à true chaque fois que l'événement mouseover est déclenché, ce qui permet au site de fonctionner comme prévu si un utilisateur utilise à la fois son écran tactile et sa souris." - Votre code ne fait pas cela. Est-ce que quelque chose m'échappe ?

0 votes

@Redtopia Merci de m'avoir prévenu ! J'ai mis à jour la réponse avec le bout de code qui a été oublié.

0 votes

Vous n'avez pas besoin de point-virgule après la déclaration de la fonction.

5voto

Bnaya Points 189

Ma solution est d'ajouter la classe css hover-active à la balise HTML, et de l'utiliser au début de tous les sélecteurs CSS avec :hover et de supprimer cette classe lors du premier événement de démarrage tactile.

http://codepen.io/Bnaya/pen/EoJlb

JS :

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML :

<html class="hover-active">

CSS :

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}

0 votes

Au lieu d'écouter un événement tactile, vous pouvez tester les éléments suivants 'ontouchstart' in window . par exemple if ('ontouchstart' in window) document.querySelector('html').classList.remove('hover-activ‌​e');

0 votes

@YuvalA. La raison pour laquelle j'attends l'événement tactile est qu'il y a des appareils avec le support du pointeur et du toucher, et si l'utilisateur utilise le pointeur, je ne veux pas supprimer le survol. Il est possible que l'utilisateur utilise le toucher et ensuite le pointeur, mais je n'ai pas grand chose à faire contre cela.

2voto

Ce que j'ai fait pour résoudre le même problème est d'avoir une détection des fonctions (j'utilise quelque chose comme ce code ), voir si onTouchMove est défini, et si c'est le cas j'ajoute la classe css "touchMode" au corps, sinon j'ajoute "desktopMode".

Ensuite, chaque fois qu'un effet de style ne s'applique qu'à un périphérique tactile ou qu'à un ordinateur de bureau, la règle css est complétée par la classe appropriée :

.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }

Modifier : Cette stratégie ajoute bien sûr quelques caractères supplémentaires à votre css. Si la taille du css vous préoccupe, vous pouvez rechercher les définitions de touchMode et desktopMode et les placer dans des fichiers différents, afin de servir un css optimisé pour chaque type de périphérique ; ou vous pouvez changer les noms de classe en quelque chose de beaucoup plus court avant de passer à la prod.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X