web-dev-qa-db-fra.com

Comment éviter les effets de survol collants des boutons sur les appareils tactiles

J'ai créé un carrousel avec un bouton précédent et un bouton suivant toujours visibles. Ces boutons ont un état de survol, ils deviennent bleus. Sur les appareils tactiles, tels que l'iPad, l'état de survol étant collant, le bouton reste bleu après l'avoir appuyé. Je ne veux pas ça.

  • Je pourrais ajouter une no-hover classe ontouchend pour chaque bouton et créermy CSS comme ceci: button:not(.no-hover):hover { background-color: blue; }, mais c'est probablement assez mauvais pour les performances et ne gère pas les appareils comme le Chromebook Pixel (qui possède à la fois un .touchscreen et une souris) correctement.

  • Je pourrais ajouter une classe touch à la documentElement et créer mon code CSScomme ceci: html:not(.touch) button:hover { background-color: blue; } Mais cela ne fonctionne pas non plus correctement sur les périphériques dotés à la fois d'une touche et d'une souris

Ce que je préférerais, c’est de supprimer l’état de survol ontouchend. Mais cela ne semble pas possible. La mise au point d'un autre élément ne supprime pas l'état de survol. Taper sur un autre élément manuellement le fait, mais je n'arrive pas à le déclencher en JavaScript.

Toutes les solutions que j'ai trouvées semblent imparfaites. Y a-t-il une solution parfaite?

116
Chris

Vous pouvez supprimer l'état de survol en supprimant temporairement le lien du DOM. Voir http://testbug.handcraft.com/ipad.html


Dans le CSS, vous avez:

:hover {background:red;}

Dans le JS, vous avez:

function fix()
{
    var el = this;
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    setTimeout(function() {par.insertBefore(el, next);}, 0)
}

Et puis dans votre HTML, vous avez:

<a href="#" ontouchend="this.onclick=fix">test</a>
49
Sjoerd Visscher

Une fois que CSS Media Queries Level 4 est implémenté, vous pourrez le faire:

@media (hover: hover) {
    button:hover {
        background-color: blue;
    }
}

Ou en anglais: "Si le navigateur prend en charge le survol correct/vrai/réel/non émulé (par exemple, a un périphérique d’entrée principal semblable à la souris), appliquez ce style lorsque buttons est survolé."

Depuis que cette partie des requêtes sur les médias, le niveau 4 n’a été implémenté que dans bleeding-Edge Chrome, j’ai écrit un polyfill pour gérer cela. En l'utilisant, vous pouvez transformer le CSS futuriste ci-dessus en:

html.my-true-hover button:hover {
    background-color: blue;
}

(Une variante de la technique .no-touch) Puis, en utilisant du code JavaScript côté client du même polyfill qui détecte la prise en charge du survol, vous pouvez activer ou désactiver la présence de la classe my-true-hover en conséquence:

$(document).on('mq4hsChange', function (e) {
    $(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});
49
cvrebert

C'est un problème commun sans solution parfaite. Le comportement de survol est utile avec une souris et surtout préjudiciable au toucher. Le problème est aggravé par les périphériques prenant en charge le toucher et la souris (simultanément, pas moins!), Tels que Chromebook Pixel et Surface.

La solution la plus propre que j'ai trouvée consiste à activer le comportement de survol si le périphérique n'est pas censé prendre en charge la saisie tactile.

var isTouch =  !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;

if( !isTouch ){
    // add class which defines hover behavior
}

Certes, vous perdez le survol sur les appareils qui pourraient le supporter. Cependant, les impacts de survol sont parfois plus importants que le lien lui-même, par ex. vous voulez peut-être afficher un menu lorsqu'un élément est survolé. Cette approche vous permet de tester l'existence du toucher et peut-être associer conditionnellement un événement différent.

J'ai testé cela sur l'iPhone, l'iPad, le Chromebook Pixel, la Surface et divers appareils Android. Je ne peux pas garantir que cela fonctionnera si une entrée tactile USB générique (telle qu'un stylet) est ajoutée au mixage.

24
Tim Medora

Avec Modernizr vous pouvez cibler vos survols spécifiquement pour les appareils sans contact:

(Remarque: cela ne fonctionne pas sur le système de snippet de StackOverflow, vérifiez le jsfiddle à la place)

/* this one is sticky */
#regular:hover, #regular:active {
  opacity: 0.5;
}

/* this one isn't */
html.no-touch #no-touch:hover, #no-touch:active {
  opacity: 0.5;
}

Notez que :active n'a pas besoin d'être ciblé avec .no-touch car il fonctionne comme prévu sur les ordinateurs de bureau et mobiles.

10
rachel

De 4 façons de gérer le survol sur le mobile : Voici un moyen d'ajouter ou de supprimer de manière dynamique une classe "can touch" dans le document en fonction du type d'entrée actuel de l'utilisateur. Cela fonctionne aussi avec les appareils hybrides où l'utilisateur peut basculer entre le toucher et une souris/trackpad:

<script>

;(function(){
    var isTouch = false //var to indicate current input type (is touch versus no touch) 
    var isTouchTimer 
    var curRootClass = '' //var indicating current document root class ("can-touch" or "")

    function addtouchclass(e){
        clearTimeout(isTouchTimer)
        isTouch = true
        if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
            curRootClass = 'can-touch'
            document.documentElement.classList.add(curRootClass)
        }
        isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
    }

    function removetouchclass(e){
        if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
            isTouch = false
            curRootClass = ''
            document.documentElement.classList.remove('can-touch')
        }
    }

    document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
    document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();

</script>
6
coco puffs
$("#elementwithhover").click(function() { 
  // code that makes element or parent slide or otherwise move out from under mouse. 

  $(this).clone(true).insertAfter($(this));
  $(this).remove();
});
5
Verard Sloggett

J'allais publier ma propre solution, mais en vérifiant si quelqu'un l'a déjà publiée, j'ai trouvé que @Rodney l'avait presque fait. Cependant, il lui manquait un dernier point crucial qui le rendait uni, du moins dans mon cas. Je veux dire, moi aussi j'ai pris la même addition/suppression de classe .fakeHover via la détection d'événements mouseenter et mouseleave, mais cela seul, en soi , agit presque exactement comme "authentique" :hover. Je veux dire: quand vous tapez sur un élément de votre table, il ne détectera pas que vous l'avez "laissé" - gardant ainsi l'état "fakehover".

Ce que j’ai fait, c’est tout simplement d’écouter sur click, aussi, donc lorsque je «tape» sur le bouton, je déclenche manuellement une mouseleave.

Si c'est mon code final:

.fakeHover {
    background-color: blue;
}


$(document).on('mouseenter', 'button.myButton',function(){
    $(this).addClass('fakeHover');
});

$(document).on('mouseleave', 'button.myButton',function(){
    $(this).removeClass('fakeHover');
});

$(document).on('button.myButton, 'click', function(){
    $(this).mouseleave();
});

De cette façon, vous conservez votre fonctionnalité hover habituelle lorsque vous utilisez une souris lorsque vous "survolez" simplement vos boutons. Eh bien, presque tout: le seul inconvénient est que, après avoir cliqué sur le bouton avec la souris, il ne sera plus dans l'état hover. Tout comme si vous aviez cliqué et enlevé rapidement le pointeur du bouton. Mais dans mon cas, je peux vivre avec ça.

2
Pere

Ajoutez ce code JS à votre page:

document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover';

maintenant dans votre CSS avant chaque stationnaire ajoutez la classe de survol comme ceci:

.hover .foo:hover {}

Si le périphérique est tactile, la classe body sera vide, sinon sa classe sera stationnaire et les règles seront appliquées!

1
Ali

Une solution qui a fonctionné pour moi:

html {
   -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

Ajoutez ce code à votre feuille de style.

Je voulais supprimer le fond gris qui apparaît sur iOS Safari lorsqu'un lien est cliqué. Mais cela semble faire plus. Maintenant, en cliquant sur un bouton (avec une pseudo-classe :hover!) S'ouvre immédiatement! Je l'ai seulement testé sur un iPad, je ne sais pas si cela fonctionnera sur d'autres appareils. 

1
Leon Vogler

Je pense avoir trouvé une solution élégante (minimum js) pour un problème similaire:

Avec jQuery, vous pouvez déclencher le survol du corps (ou de tout autre élément), en utilisant .mouseover()

Donc, j'attache simplement un gestionnaire this à l'événement ontouchend de l'élément, comme ceci:

var unhover = function() {
  $("body").mousover();  
};
.hoverable {
  width: 100px;
  height: 100px;
  background: teal;
  cursor: pointer;
}

.hoverable:hover {
  background: pink;
}
<div class="hoverable" ontouchend={unhover}></div>

Ceci, cependant, ne supprime que: la pseudo-classe de survol de l'élément après le déclenchement d'un autre événement tactile, comme un glissement ou un autre contact 

1
Ian Averno

Vous pouvez remplacer l'effet de survol pour les périphériques qui ne prennent pas en charge le survol. Comme:

.my-thing {
    color: #BADA55;
}

.my-thing:hover {
    color: hotpink;
}

@media (hover: none) {
    .my-thing {
        color: #BADA55;
    }
}

Testé et vérifié sur iOS 12

Chapeau de chapeau à https://stackoverflow.com/a/50285058/178959 pour le signaler.

1
OrganicPanda

C’est ce que j’ai trouvé jusqu’à présent après avoir étudié le reste des réponses. Il devrait pouvoir prendre en charge les utilisateurs tactiles, souris uniquement ou hybrides.

Créez une classe de survol distincte pour l'effet de survol. Par défaut, ajoutez cette classe de survol à notre bouton.

Nous ne voulons pas détecter la présence du support tactile et désactiver tous les effets de survol dès le début. Comme mentionné par d'autres, les appareils hybrides gagnent en popularité; les personnes peuvent avoir une assistance tactile mais vouloir utiliser une souris et inversement. Par conséquent, ne supprimez la classe de survol que lorsque l'utilisateur appuie réellement sur le bouton.

Le problème suivant est que faire si l'utilisateur veut utiliser une souris après avoir appuyé sur le bouton? Pour résoudre ce problème, nous devons trouver un moment opportun pour rajouter la classe de survol que nous avons supprimée.

Cependant, nous ne pouvons pas le rajouter immédiatement après l'avoir retiré, car l'état de survol est toujours actif. Nous ne voulons peut-être pas détruire et recréer l’ensemble du bouton.

J'ai donc pensé à utiliser un algorithme d'occupation en attente (en utilisant setInterval) pour vérifier l'état de survol. Une fois que le survol est désactivé, nous pouvons alors rajouter la classe de survol et arrêter l’attente, ce qui nous ramène à l’état initial dans lequel l’utilisateur peut utiliser la souris ou le toucher.

Je sais que l'attente n'est pas terrible, mais je ne suis pas sûr qu'il y ait un événement approprié. J'ai envisagé de le rajouter dans l'événement Mouseleave, mais ce n'était pas très robuste. Par exemple, lorsqu'une alerte apparaît après que vous avez appuyé sur le bouton, la position de la souris est modifiée, mais l'événement de suppression de la souris n'est pas déclenché.

var button = document.getElementById('myButton');

button.ontouchstart = function(e) {
  console.log('ontouchstart');
  $('.button').removeClass('button-hover');
  startIntervalToResetHover();
};

button.onclick = function(e) {
  console.log('onclick');
}

var intervalId;

function startIntervalToResetHover() {
  // Clear the previous one, if any.
  if (intervalId) {
    clearInterval(intervalId);
  }
  
  intervalId = setInterval(function() {
    // Stop if the hover class already exists.
    if ($('.button').hasClass('button-hover')) {
      clearInterval(intervalId);
      intervalId = null;
      return;
    }
    
    // Checking of hover state from 
    // http://stackoverflow.com/a/8981521/2669960.
    var isHovered = !!$('.button').filter(function() {
      return $(this).is(":hover");
    }).length;
    
    if (isHovered) {
      console.log('Hover state is active');
    } else {
      console.log('Hover state is inactive');
      $('.button').addClass('button-hover');
      console.log('Added back the button-hover class');
      
      clearInterval(intervalId);
      intervalId = null;
    }
  }, 1000);
}
.button {
  color: green;
  border: none;
}

.button-hover:hover {
  background: yellow;
  border: none;
}

.button:active {
  border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='myButton' class='button button-hover'>Hello</button>

Edit: Une autre approche que j'ai essayée consiste à appeler e.preventDefault() dans ontouchstart ou ontouchend. Il semble arrêter l’effet de survol lorsque vous appuyez sur le bouton, mais également son animation et empêcher la fonction onclick d’appeler lorsque vous appuyez sur le bouton. Vous devez donc les appeler manuellement dans le gestionnaire ontouchstart ou ontouchend. Pas une solution très propre.

1
Kevin Lee

Je pourrais ajouter un ontouchend de classe no-hover pour chaque bouton et rendre mon code CSS comme> ceci: bouton: not (.no-hover): hover {background-color: bleu; } mais c'est probablement assez mauvais pour la performance, et ne gère pas les> appareils comme le Chromebook Pixel (qui a un écran tactile et une souris)> correctement.

C'est le bon point de départ. Étape suivante: Appliquer/supprimer la classe nohover sur les événements suivants (démonstration avec jQuery)

buttonelement
 .on("touchend touchcancel",function(){$(this).addClass("nohover")})
 .on("touchstart mouseover",function({$(this).removeClass("nohover")});   

Remarque: Si vous souhaitez appliquer d’autres classes à l’élément button, le: not (.nohover) dans le fichier CSS ne fonctionnera plus comme prévu. Ensuite, vous devez ajouter une définition séparée avec une valeur par défaut et une balise! Important pour écraser le style de survol: .nohover {background-color: white! important} 

Cela devrait même gérer correctement des appareils comme le Chromebook Pixel (qui possède à la fois un écran tactile et une souris)! Et je ne pense pas que cela tue beaucoup les performances ...

1
Sascha Hendel

Cela m'a été utile: link

function hoverTouchUnstick() {
    // Check if the device supports touch events
    if('ontouchstart' in document.documentElement) {
        // Loop through each stylesheet
        for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
            var sheet = document.styleSheets[sheetI];
            // Verify if cssRules exists in sheet
            if(sheet.cssRules) {
                // Loop through each rule in sheet
                for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
                    var rule = sheet.cssRules[ruleI];
                    // Verify rule has selector text
                    if(rule.selectorText) {
                        // Replace hover psuedo-class with active psuedo-class
                        rule.selectorText = rule.selectorText.replace(":hover", ":active");
                    }
                }
            }
        }
    }
}
1
mineroot

Vous pouvez essayer de cette façon.

javascript:

var isEventSupported = function (eventName, elementName) {
    var el = elementName ? document.createElement(elementName) : window;
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported && el.setAttribute) {
        el.setAttribute(eventName, 'return;');
        isSupported = typeof el[eventName] == 'function';
    }
    el = null;
    return isSupported;
};

if (!isEventSupported('touchstart')) {
    $('a').addClass('with-hover');
}

css:

a.with-hover:hover {
  color: #fafafa;
}
0
Lorenzo Polidori

Vous pouvez définir background-color sur :active et donner à :focus le fond par défaut.

si vous définissez background-color via onfocus/ontouch, le style de couleur reste inchangé une fois que l'état :focus a disparu.
Vous devez également réinitialiser onblur pour restaurer bg par défaut lorsque la mise au point est perdue.

0
G-Cyr

Basé sur la réponse de Darren Cooks, qui fonctionne également si vous déplacez votre doigt sur un autre élément.

Voir le doigt de l'élément de recherche est activé lors d'un événement tactile

jQuery(function() {
    FastClick.attach(document.body);
});
// Prevent sticky hover effects for buttons on touch devices
// From https://stackoverflow.com/a/17234319
//
//
// Usage:
// <a href="..." touch-focus-fix>..</a>
//
// Refactored from a directive for better performance and compability
jQuery(document.documentElement).on('touchend', function(event) {
  'use strict';

  function fix(sourceElement) {
    var el = $(sourceElement).closest('[touch-focus-fix]')[0];
    if (!el) {
      return;
    }
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    par.insertBefore(el, next);
  }

  fix(event.target);
  var changedTouch = event.originalEvent.changedTouches[0];
  // http://www.w3.org/TR/2011/WD-touch-events-20110505/#the-touchend-event
  if (!changedTouch) {
    return;
  }
  var touchTarget = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
  if (touchTarget && touchTarget !== event.target) {
    fix(touchTarget);
  }
});

Codepen Demo

0
jantimon

J'ai une solution intéressante que je voudrais partager . Tout d'abord, vous devez détecter si l'utilisateur est sur un téléphone mobile comme ceci:

var touchDevice = /ipad|iphone|Android|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase());

Ensuite, ajoutez simplement:

if (!touchDevice) {
    $(".navbar-ul").addClass("hoverable");
}

Et en CSS:

.navbar-ul.hoverable li a:hover {
    color: #fff;
}
0
Michał Reszka

Ce que j’ai fait jusqu’à présent dans mes projets a été d’annuler les modifications de :hover sur les appareils tactiles:

.myhoveredclass {
    background-color:green;
}
.myhoveredclass:hover {
    background-color:red;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
    .myhoveredclass:hover, .myhoveredclass:active, .myhoveredclass:focus {
        background-color:green;
    }
}

Tous les noms de classes et couleurs nommées uniquement à des fins de démonstration ;-)

0
z00l

Cela fonctionne parfaitement en 2 étapes.

  1. Définissez votre balise body comme ceci <body ontouchstart="">. Je ne suis pas un fan de ce "bidouillage", mais cela permet à Safari sur iOS de réagir instantanément au toucher. Je ne sais pas comment, mais ça marche.

  2. Configurez votre classe au toucher comme ceci:

    // I did this in SASS, but this should work with normal CSS as well
    
    // Touchable class
    .example {
    
        // Default styles
        background: green;
    
        // Default hover styles 
        // (Think of this as Desktop and larger)
        &:hover {
            background: yellow;
        }
    
        // Default active styles
        &:active {
            background: red;
        }
    
        // Setup breakpoint for smaller device widths
        @media only screen and (max-width: 1048px) {
    
            // Important!
            // Reset touchable hover styles
            // You may want to use the same exact styles as the Default styles
            &:hover {
                background: green;
            }
    
            // Important!
            // Touchable active styles
            &:active {
                background: red;
            }
        }
    }
    

Vous voudrez peut-être également supprimer toute animation de votre classe tactile. Android Chrome semble être un peu plus lent que iOS.

Cela entraînera également l'application de l'état actif si l'utilisateur fait défiler la page tout en touchant votre classe.

0
Michael

Certains des problèmes de blocage :hover:focus:active sur les appareils mobiles peuvent être dus à l'absence de <meta name="viewport" content="width=device-width"> lorsque les navigateurs tentent de manipuler l'écran.

0
GEkk

Je suis passé par le même problème, mon application était compatible avec toutes les tailles d’écran et avait beaucoup d’effets de survol dans le desktop-screen-size/appareils basés sur la souris, et plus tard j’ai réalisé que les appareils tactiles provoquait un problème connu sous le nom de post-it et c'était un obstacle pour que l'application fonctionne correctement pour les utilisateurs d'appareils tactiles.

Nous utilisions SCSS dans notre application. et j'ai défini un mixin pour prendre soin de l'appareil tactile.

@mixin hover-support {
  @media not all and (pointer: coarse) {
    &:hover {
      @content;
    }
  }
}

et puis j'ai placé toutes mes classes css sous l'extrait mentionné ci-dessous.

   @include hover-support() {
    // Your css-classes or css that you want to apply on those devices that support hover.
   }

Par exemple, nous avions une classe pour animer une icône, et qui se déclenchait une fois que nous survolions l'icône comme vous pouvez le voir à partir de css, mais dans un périphérique tactile, il était affecté par l'effet de survol collant, puis je la plaçais à l'intérieur - @ include hover-support () afin de garantir que le survol ne s'applique qu'aux périphériques prenant en charge le survol.

@include hover-support() {
  .animate-icon {
    -webkit-transition: all 0.2s;
    transition: all 0.2s;
    &:hover {
      transform: scale(1.3);
      filter: brightness(85%);
      cursor: pointer;
    }
  }
}
0
Divyanshu Rawat

Cela a fonctionné pour moi: mettez le style en vol stationnaire dans une nouvelle classe

.fakehover {background: red}

Puis ajoutez/supprimez la classe selon vos besoins

$(".someclass > li").on("mouseenter", function(e) {
  $(this).addClass("fakehover");
});
$(".someclass > li").on("mouseleave", function(e) {
  $(this).removeClass("fakehover");
});

Répétez cette procédure pour les événements touchstart et touchend. Ou quels que soient les événements pour lesquels vous souhaitez obtenir le résultat souhaité, par exemple, je souhaitais que l’effet de survol soit basculé sur un écran tactile.

0
Rodney