: la pseudo-classe active ne fonctionne pas en safari mobile


109

Dans Webkit sur iPhone / iPad / iPod, la spécification du style d'une pseudo-classe: active pour une <a>balise ne se déclenche pas lorsque vous appuyez sur l'élément. Comment puis-je déclencher cela? Exemple de code:

<style> 
a:active { 
    background-color: red;
}
</style>
<!-- snip -->
<a href="#">Click me</a>

Réponses:


238
<body ontouchstart="">
    ...
</body>

Appliqué une seule fois, contrairement à chaque élément de bouton, il semble que tous les boutons de la page soient corrigés. Vous pouvez également utiliser cette petite bibliothèque JS appelée « Fastclick ». Il accélère les événements de clic sur les appareils tactiles et prend également en charge ce problème.


19
N'est-il pas suffisant d'ajouter simplement ontouchstartsans le =""? (HTML5)
feklee

2
Pour tous les futurs lecteurs, j'ai posté une démo ici: http://jsbin.com/EjiWILe/3/ . Vérifiez la fonctionnalité de votre appareil iOS.
mhulse

6
Pouvez-vous expliquer pourquoi vous avez appliqué un auditeur vide au corps? Comment ça fonctionne?
Maurizio Battaghini

2
J'utilisais: focus, et cela fonctionne aussi pour cela. Merci pour la magie.
TecBrat

Je reçois des gels de page sur iOS 9.1 et 9.3 en utilisant cette méthode ... ces versions avaient un bogue js, mais ont toujours 0,5% d'utilisateurs sur ces systèmes d'exploitation
Ruskin

55

Comme d'autres réponses l'ont indiqué, iOS Safari ne déclenche pas la :activepseudo-classe à moins qu'un événement tactile ne soit attaché à l'élément, mais jusqu'à présent, ce comportement a été "magique". Je suis tombé sur ce petit texte de présentation de la bibliothèque des développeurs Safari qui l'explique (c'est moi qui souligne):

Vous pouvez également utiliser la -webkit-tap-highlight-colorpropriété CSS en combinaison avec la définition d'un événement tactile pour configurer les boutons afin qu'ils se comportent de la même manière que le bureau. Sur iOS, les événements de souris sont envoyés si rapidement que l'état actif ou actif n'est jamais reçu. Par conséquent, le :activepseudo état est déclenché uniquement lorsqu'un événement tactile est défini sur l'élément HTML, par exemple lorsque ontouchstart est défini sur l'élément comme suit:

<button class="action" ontouchstart=""
        style="-webkit-tap-highlight-color: rgba(0,0,0,0);">
    Testing Touch on iOS
</button>

Désormais, lorsque le bouton est appuyé et maintenu sur iOS, le bouton prend la couleur spécifiée sans que la couleur grise transparente environnante n'apparaisse.

En d'autres termes, définir un ontouchstartévénement (même s'il est vide) indique explicitement au navigateur de réagir aux événements tactiles.

À mon avis, c'est un comportement imparfait, et remonte probablement à l'époque où le Web «mobile» était pratiquement inexistant (jetez un œil à ces captures d'écran sur la page liée pour voir ce que je veux dire), et tout était orienté souris. Il est intéressant de noter que d'autres navigateurs mobiles plus récents, comme sur Android, affichent très bien le pseudo-état `: actif 'au toucher, sans aucun piratage comme ce qui est nécessaire pour iOS.

(Remarque: si vous souhaitez utiliser vos propres styles personnalisés sur iOS, vous pouvez également désactiver la boîte translucide grise par défaut qu'iOS utilise à la place du :activepseudo-état en utilisant la -webkit-tap-highlight-colorpropriété CSS, comme expliqué dans la même page liée ci-dessus .)


Après quelques expérimentations, la solution attendue consistant à définir un ontouchstartévénement sur l' <body>élément vers lequel tous les événements tactiles se retrouvent ensuite ne fonctionne pas pleinement. Si l'élément est visible dans la fenêtre lorsque la page se charge, cela fonctionne bien, mais faire défiler vers le bas et appuyer sur un élément qui était hors de la fenêtre ne déclenche pas le :activepseudo-état comme il se doit. Donc, au lieu de

<!DOCTYPE html>
<html><body ontouchstart></body></html>

attachez l'événement à tous les éléments au lieu de vous fier à l'événement remontant au corps (en utilisant jQuery):

$('body *').on('touchstart', function (){});

Cependant, je ne suis pas conscient de ses implications sur les performances, alors méfiez-vous.


EDIT: Il y a un sérieux défaut avec cette solution: même toucher un élément en faisant défiler la page activera le :activepseudo état. La sensibilité est trop forte. Android résout ce problème en introduisant un très petit délai avant l'affichage de l'état, qui est annulé si la page défile. À la lumière de cela, je suggère de ne l'utiliser que sur certains éléments. Dans mon cas, je développe une application Web à utiliser sur le terrain, qui consiste essentiellement en une liste de boutons pour parcourir les pages et soumettre des actions. Parce que la page entière est à peu près des boutons dans certains cas, cela ne fonctionnera pas pour moi. Vous pouvez cependant définir le :hoverpseudo-état à remplir pour cela à la place. Après avoir désactivé la boîte grise par défaut, cela fonctionne parfaitement.


6
Excellente explication du pourquoi et pas seulement du comment ! Merci!
coderfin

6
Voté pour avoir donné la compréhension aux développeurs, pas seulement une recette «magique». C'est infiniment plus informatif que les autres; merci d'avoir écrit ceci pour nous.
user508633

Voté pour une explication détaillée + mises en garde. Cela devrait être la bonne réponse.
Maître des canards

39

Ajoutez un gestionnaire d'événements pour ontouchstart dans votre balise <a>. Cela fait fonctionner le CSS comme par magie.

<a ontouchstart="">Click me</a>

3
Désolé, c'est une vieille réponse, mais pourquoi cela fonctionne-t-il? Cela ne me dérange pas «par magie» comme raison, mais si vous pouviez expliquer pourquoi cela fonctionne, j'aimerais lire plus de détails.
mhulse

1
@mhulse J'aimerais aussi savoir pourquoi.
Jesse Rusak

Ha! Nous sommes dans le même bateau. Je vais essayer de faire quelques recherches et poster ici un lien vers des documents (semi) officiels sur le sujet. J'adore le fait que cette technique fonctionne, je me demande simplement pourquoi?
mhulse

7

Cela fonctionne pour moi:

document.addEventListener("touchstart", function() {},false);

Remarque: si vous faites cette astuce, il vaut également la peine de supprimer la couleur de mise en surbrillance par défaut que Mobile Safari applique en utilisant la règle CSS suivante.

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

5

Depuis le 8 décembre 2016, la réponse acceptée ( <body ontouchstart="">...</body>) ne fonctionne pas pour moi sur Safari 10 (iPhone 5s): ce hack ne fonctionne que pour les éléments qui étaient visibles au chargement de la page.

Cependant, en ajoutant:

<script type='application/javascript'>
  document.addEventListener("touchstart", function() {}, false);
</script>

le headfonctionne comme je le souhaite, avec l'inconvénient que désormais tous les événements tactiles pendant le défilement déclenchent également le :activepseudo-état sur les éléments touchés. (Si cela pose un problème pour vous, vous pouvez envisager de FighterJet :hover solution contournement .)


4
//hover for ios
-webkit-tap-highlight-color: #ccc;

Cela fonctionne pour moi, ajoutez à votre CSS sur l'élément que vous souhaitez mettre en évidence


3

Utilisez-vous toutes les pseudo-classes ou une seule? Si vous en utilisez au moins deux, assurez-vous qu'ils sont dans le bon ordre ou qu'ils se cassent tous:

a:link
a:visited
a:hover
a:active

..dans cet ordre. De plus, si vous utilisez simplement: active, ajoutez un lien:, même si vous ne le stylisez pas.


bon conseil sur l'importance de l'ordre lors de la création de pseudo-classes.
brianarpie

1

J'ai publié un outil qui devrait résoudre ce problème pour vous.

En apparence, le problème semble simple, mais en réalité, le comportement tactile et clic doit être personnalisé de manière assez approfondie, y compris les fonctions de délai d'expiration et des choses comme «ce qui se passe lorsque vous faites défiler une liste de liens» ou «ce qui se passe lorsque vous appuyez sur le lien, puis éloignez la souris / le doigt de la zone active "

Cela devrait tout résoudre en même temps: https://www.npmjs.com/package/active-touch

Vous devrez soit affecter vos styles: actifs à la classe .active, soit choisir votre propre nom de classe. Par défaut, le script fonctionnera avec tous les éléments de lien, mais vous pouvez l'écraser avec votre propre tableau de sélecteurs.

Commentaires et contributions honnêtes et utiles très appréciés!


2
Cette bibliothèque ajoute 9 écouteurs d'événements (non passifs) à chaque élément de lien mais n'appelle pas removeEventListener - ce serait un gros défaut pour les applications à page unique, je pense
Drenai

1
Merci Ryan. Je ne supporte ni n'utilise ce code depuis plus d'un an; Je vais le retirer. Après réflexion et expérimentation, j'ai changé la conception de l'application au lieu d'essayer de forcer des interactions non prises en compte ou non prises en compte par les navigateurs.
dmitrizzle

1

Pour ceux qui ne souhaitent pas utiliser ontouchstart, vous pouvez utiliser ce code

<script>
 document.addEventListener("touchstart", function(){}, true);
</script>

1

J'ai essayé cette réponse et ses variantes, mais aucune ne semblait fonctionner de manière fiable (et je n'aime pas me fier à la «magie» pour des choses comme celle-ci). J'ai donc fait ce qui suit à la place, qui fonctionne parfaitement sur toutes les plates-formes, pas seulement Apple:

  1. Déclarations CSS qui changé le nom utilisé :activepour .active.
  2. Création d'une liste de tous les éléments affectés et ajout de pointerdown/mousedown/touchstartgestionnaires d'événements pour appliquer la .activeclasse et les pointerup/mouseup/touchendgestionnaires d'événements pour le supprimer. Utilisation de jQuery:

    let controlActivationEvents = window.PointerEvent ? "pointerdown" : "touchstart mousedown";
    let controlDeactivationEvents = window.PointerEvent ? "pointerup pointerleave" : "touchend mouseup mouseleave";
    
    let clickableThings = '<comma separated list of selectors>';
    $(clickableThings).on(controlActivationEvents,function (e) {
        $(this).addClass('active');
    }).on(controlDeactivationEvents, function (e) {
        $(this).removeClass('active');
    });

C'était un peu fastidieux, mais maintenant j'ai une solution qui est moins vulnérable à la casse entre les versions d'Apple OS. (Et qui a besoin de quelque chose comme cette rupture?)


Je pense que cela répond à la question. Il ne semble y avoir aucun moyen fiable de faire fonctionner la pseudo-classe: active sur Safari mobile. Mais cela peut être émulé de manière fiable en utilisant la solution que j'ai donnée. J'ai fait cela dans mon application et cela fonctionne parfaitement sur toutes les plateformes. Et j'ai ajouté du code à titre de clarification.
daffinm

0

Une solution consiste à s'appuyer sur :targetau lieu de :active:

<style> 
a:target { 
    background-color: red;
}
</style>
<!-- snip -->
<a id="click-me" href="#click-me">Click me</a>

Le style sera déclenché lorsque l'ancre est ciblée par l'url actuelle, qui est robuste même sur mobile. L'inconvénient est que vous avez besoin d'un autre lien pour effacer l'ancre dans l'url. Exemple complet:

a:target { 
    background-color: red;
}
<a id="click-me" href="#click-me">Click me</a>
<a id="clear" href="#">Clear</a>


-3

Pas de 100% lié à cette question, mais vous pouvez également utiliser le hack des frères et sœurs css pour y parvenir

HTML

<input tabindex="0" type="checkbox" id="145"/>
<label for="145"> info</label>
<span> sea</span>

SCSS

input {
    &:checked + label {
      background-color: red;
    }
  }

Si vous souhaitez utiliser une info-bulle html / css pure

span {
  display: none;
}
input {
    &:checked ~ span {
      display: block;
    }
  }

-6
<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="utf-8">

<title></title>

<style>
        a{color: red;}
        a:hover{color: blue;}
</style>
</head>

<body>

        <div class="main" role="main">
                <a href="#">Hover</a>
        </div>

</body>
</html>

1
Selon la FAQ, les signatures et l'auto-promotion ne sont pas autorisées.
LittleBobbyTables - Au Revoir
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.