Comment prévenir les effets de vol stationnaire collant pour les boutons sur les dispositifs tactiles

j'ai créé un carrousel avec un précédent et un bouton "suivant" qui sont toujours visibles. Ces boutons ont un État de vol stationnaire, ils deviennent bleus. Sur les appareils tactiles, comme l'iPad, l'état de vol stationnaire est collant, donc le bouton reste bleu après l'avoir tapé. Je n'en veux pas.

  • je pourrais ajouter un no-hover classe ontouchend pour chaque bouton, et faire mon CSS comme ceci: button:not(.no-hover):hover { background-color: blue; } mais c'est probablement assez mauvais pour la performance, et ne gérer les périphériques comme le Pixel Chromebook (qui a à la fois écran tactile et d'une souris) correctement.

  • je pourrais ajouter une classe touch au documentElement et faire mon CSS comme ceci: html:not(.touch) button:hover { background-color: blue; } mais cela ne fonctionne pas non plus correctement sur les appareils à la fois tactile et souris.

ce que je préférerais, c'est supprimer l'état de vol stationnaire ontouchend . Mais il ne semble pas que ce soit possible. Concentrer un autre élément ne supprime pas l'état de pointage. Taper un autre élément le fait manuellement, mais je ne peux pas le déclencher en JavaScript.

toutes les solutions que j'ai trouvées semblent imparfaites. Est-il une solution parfaite?

107
demandé sur Rahul 2013-06-21 15:10:40

19 réponses

vous pouvez supprimer l'état de hover en retirant temporairement le lien du DOM. Voir http://testbug.handcraft.com/ipad.html


dans la 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>
48
répondu Darren Cook 2014-10-17 08:39:00

une fois css Media Queries Level 4 mis en œuvre, vous pourrez le faire:

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

Ou en anglais: "Si le navigateur prend en charge bon/vrai/vrai/non émulé en vol stationnaire (par exemple, a une souris comme principal dispositif d'entrée), puis d'appliquer ce style button s sont planait au-dessus."

étant donné que cette partie de Media Queries Level 4 N'a été implémentée jusqu'à présent que dans bleeding-edge Chrome, j'ai écrit un polyfill pour faire face à cela. En l'utilisant, vous pouvez transformer les CSS futuristes ci-dessus en:

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

(une variante de la technique .no-touch ) et ensuite en utilisant un JavaScript côté client du même polyfill qui détecte le support pour le vol stationnaire, vous pouvez basculer 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);
});
44
répondu cvrebert 2015-01-21 03:01:24

C'est un problème courant sans solution parfaite. Passez comportement est utile avec une souris et surtout préjudiciable avec touch. Le problème est aggravé par les appareils qui supportent le toucher et la souris (simultanément, pas moins!) comme le Pixel Chromebook et la Surface.

la solution la plus propre que j'ai trouvée est de n'activer le comportement de vol stationnaire que si le périphérique n'est pas considéré comme supportant l'entrée touch.

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

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

accordé, vous perdez le vol stationnaire sur les dispositifs qui peut charge. Cependant, il arrive parfois que les impacts de vol stationnaire soient plus importants que le lien lui-même, par exemple, vous voulez peut-être afficher un menu lorsqu'un élément est en vol stationnaire. Cette approche permet de tester l'existence de toucher et peut-être conditionnellement attacher un événement différent.

j'ai testé cela sur l'iPhone, iPad, Chromebook Pixel, Surface, et une variété d'appareils Android. Je ne peux pas garantir que cela fonctionnera quand un générique USB saisie tactile (stylet) est ajouté au mélange.

24
répondu Tim Medora 2013-10-31 19:50:12

avec Modernizr vous pouvez cibler vos capots spécifiquement pour les appareils sans contact:

(Note: Ceci ne fonctionne pas sur le système snippet de StackOverflow, cochez 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 parce qu'il fonctionne comme prévu à la fois sur mobile et de bureau.

10
répondu rachel 2018-03-25 18:46:04

à Partir de 4 façons de traiter avec collant passez sur mobile : Voici une façon dynamique d'ajouter ou de supprimer une " can touch " la classe du document selon le type d'entrée de l'utilisateur. Il fonctionne aussi bien avec des appareils hybrides où l'utilisateur peut passer du toucher à la 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
répondu coco puffs 2016-07-21 21:20:26
$("#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
répondu Verard Sloggett 2015-11-18 02:18:25

j'allais poster ma propre solution, mais de vérifier si quelqu'un l'a déjà posté, j'ai trouvé que @Rodney presque fait. Cependant, il a manqué un dernier point crucial qui l'a rendu inutile, du moins dans mon cas. Je veux dire, j'ai aussi pris le même .fakeHover ajout de classe / suppression via mouseenter et mouseleave détection d'événement, mais que seul, en soi , agit presque exactement comme "authentique" :hover . Je veux dire: quand vous touchez un élément dans votre table, il ne sera pas détectez que vous l'avez" quitté "- gardant ainsi l'état de" fakehover".

ce que j'ai fait était simplement d'écouter sur click , aussi, quand je" appuie "sur le bouton, je tire manuellement un 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 gardez votre habituelle hover fonctionnalité lors de l'utilisation d'une souris quand simplement "planer" sur votre bouton. Eh bien, presque tout: le seul inconvénient d'une façon ou d'une autre est que, après avoir cliqué sur le bouton avec la souris, il ne sera pas dans hover état. Un peu comme si vous cliquiez et retiriez rapidement le pointeur du bouton. Mais dans mon cas, je peux vivre avec ça.

2
répondu Pere 2016-06-01 18:02:16

il a été utile pour moi: lien

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
répondu mineroot 2015-07-06 14:55:40

ajouter ce code JS à votre page:

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

maintenant dans votre CSS avant chaque vol stationnaire ajouter la classe de vol stationnaire comme ceci:

.hover .foo:hover {}

si l'appareil est touch, la classe de carrosserie sera vide, sinon sa classe sera en vol stationnaire et les règles sont appliquées!

1
répondu Ali 2015-12-06 00:22:16

je pourrais ajouter une classe sans hover ontouchend pour chaque bouton, et faire mon CSS comme > ce 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 Pixel Chromebook (qui a à la fois un écran tactile et une souris) >correctement.

C'est le bon point de départ. Prochaine étape: appliquer/enlever "nohover" sur la suite des événements de démonstration (avec jQuery)

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

avis: Si vous souhaitez appliquer d'autres classes au bouttonelement, le :pas(.nohover) dans le CSS ne fonctionnera plus comme prévu. Que vous devez ajouter à la place une définition séparée avec la valeur par défaut et !étiquette importante pour écraser le style de vol stationnaire: .nohover{ background-color: white !important}

cela devrait même manipuler correctement des appareils comme le Chromebook Pixel (qui a à la fois un écran tactile et une souris)! Et je ne pense pas que cela l'un des principaux la performance de tueur...

1
répondu Sascha Hendel 2016-03-10 14:39:56

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 me débarrasser du fond gris qui apparaît sur iOS Safari quand un lien est cliqué. Mais il semble faire plus. Maintenant, en cliquant sur un bouton (avec un pseudo-classeur :hover !) est ouvert tout de suite! Je ne l'ai testé que sur un iPad, Je ne sais pas si ça marchera sur d'autres appareils.

1
répondu Leon Vogler 2016-03-19 18:04:20

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

créer une classe de vol stationnaire distincte pour l'effet de vol stationnaire. Par défaut, ajoutez cette classe hover à notre bouton.

nous ne voulons pas détecter la présence de support tactile et désactiver tous les effets de hover dès le début. Comme mentionné par d'autres, les appareils hybrides gagnent en popularité; les gens peut avoir le soutien tactile mais veulent utiliser une souris et vice versa. Par conséquent, ne supprimez la classe hover que lorsque l'utilisateur touche effectivement le bouton.

le problème suivant est, que faire si l'utilisateur veut revenir à l'utilisation d'une souris après avoir touché le bouton? Pour résoudre cela, nous avons besoin de trouver un moment opportun pour ajouter en arrière la classe de vol stationnaire que nous avons enlevée.

Cependant, nous ne pouvons pas l'ajouter immédiatement après l'avoir enlevé, parce que l'état de vol stationnaire est toujours actif. Nous ne voulons peut-être pas détruire et recréer le bouton entier.

donc, j'ai pensé à utiliser un algorithme de busy-waiting (en utilisant setInterval) pour vérifier l'état de vol stationnaire. Une fois que l'état de vol stationnaire est désactivé, nous pouvons alors ajouter en arrière la classe de vol stationnaire et arrêter le busy-waiting, nous ramenant à l'état d'origine où l'utilisateur peut utiliser la souris ou le toucher.

je sais que l'attente n'est pas si terrible, mais je ne suis pas sûr qu'il y ait un événement approprié. J'ai réfléchi à ajouter dans le mouseleave événement, mais il n'était pas très robuste. Par exemple, lorsqu'une alerte apparaît après que le bouton est touché, la position de la souris change mais l'événement mouseleave 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é est d'appeler e.preventDefault() dans ontouchstart ou ontouchend. Il semble arrêter l'effet de vol stationnaire lorsque le bouton est touché, mais il arrête également la cliquez sur le bouton animation et empêche la fonction onclick d'être appelée lorsque le bouton est touché, de sorte que vous devez appeler ceux manuellement dans le handler ontouchstart ou ontouchend. Pas une solution très propre.

1
répondu Kevin Lee 2016-07-28 10:00:14

vous pouvez définir le fond-couleur sur :active état et donner :focus le fond de défaut.

si vous définissez background-color via onfocus/ontouch , le style de couleur reste une fois :focus l'État a disparu.

Vous avez besoin d'une réinitialisation sur onblur aussi bien pour restaurer defaut bg quand la mise au point est perdue.

0
répondu G-Cyr 2013-06-21 13:43:12

cela a fonctionné pour moi: mettre le style hover dans une nouvelle classe

.fakehover {background: red}

Ajouter / Supprimer la classe comme et quand requis

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

Répétez l'opération pour les évènements touchstart et touchend événements. Ou des événements que vous aimez pour obtenir le résultat souhaité, par exemple je voulais hover effet d'être basculées sur un écran tactile.

0
répondu Rodney 2015-07-17 11:07:08

je pense que j'ai trouvé une solution élégante (minimum js) pour un problème similaire:

en utilisant jQuery, vous pouvez déclencher le vol stationnaire sur le corps( ou tout autre élément), en utilisant .mouseover()

donc j'attache simplement ce handler à l'événement ontouchend de l'élément comme ça:

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: hover pseudoclass de l'élément après une autre touche événement a été déclenché, comme le glisser ou une autre touche

0
répondu Ian Averno 2016-04-24 19:56:50

basé sur Darren Cooks réponse qui fonctionne aussi si vous avez déplacé votre doigt sur un autre élément.

Voir Trouver l'élément de doigt pendant une touchend événement

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

Démonstration De Codepen

0
répondu jantimon 2017-05-23 11:55:10

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
répondu Lorenzo Polidori 2016-09-30 08:44:40

ce que j'ai fait jusqu'à présent dans mes projets était de revenir à la :hover changements sur les dispositifs 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 classe et les couleurs nommées juste pour des fins de démonstration ;-)

0
répondu z00l 2016-12-25 20:47:51

Cela fonctionne parfaitement en 2 étapes.

  1. Définissez votre étiquette corporelle comme ceci <body ontouchstart=""> . Je ne suis pas un fan de ce "hack", mais il permet Safari sur iOS de réagir à des touches instantanément. Vous ne savez pas comment, mais ça marche.

  2. mettez en place votre classe touchable 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 pouvez supprimer n'importe quelle animation sur votre classe touchable aussi bien. Android Chrome semble être un peu plus lent que iOS.

cela se traduira également par l'état actif appliqué si l'utilisateur défile la page en touchant votre classe.

0
répondu Michael 2017-09-02 21:48:58