Comment simuler un survol avec une touche dans les navigateurs tactiles?

120

Avec du HTML comme celui-ci:

<p>Some Text</p>

Ensuite, quelques CSS comme ceci:

p {
  color:black;
}

p:hover {
  color:red;
}

Comment puis-je autoriser une longue pression sur un appareil tactile pour répliquer le survol? Je peux changer le balisage / utiliser JS, etc., mais je ne peux pas penser à un moyen facile de le faire.

Rich Bradshaw
la source
Je pense spécifiquement à iPhone OS - c'est une démo d'animation CSS sur laquelle pour beaucoup, il est facile d'utiliser le survol plutôt que de cliquer pour démarrer.
Rich Bradshaw

Réponses:

172

OK, je l'ai trouvé! Cela implique de modifier légèrement le CSS et d'ajouter du JS.

Utilisation de jQuery pour simplifier les choses:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

En anglais: lorsque vous démarrez ou terminez une touche, activez hover_effectou désactivez le cours.

Ensuite, dans votre HTML, ajoutez un survol de classe à tout ce avec quoi vous voulez que cela fonctionne. Dans votre CSS, remplacez toute instance de:

element:hover {
    rule:properties;
}

avec

element:hover, element.hover_effect {
    rule:properties;
}

Et juste pour plus d'utilité, ajoutez également ceci à votre CSS:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

Pour arrêter le navigateur vous demandant de copier / enregistrer / sélectionner l'image ou autre.

Facile!

Rich Bradshaw
la source
C'est bien. J'ai divisé la fonction domready car togglecela gâcherait les choses si l'utilisateur touche un élément et traîne vers un autre (par exemple, s'il a touché le mauvais élément et essaie d'annuler le toucher)
Jacksonkr
J'ai supprimé touchendet preventDefault()dans mon site Web et cela fonctionne bien, mais maintenant les menus ne se ferment pas automatiquement en appuyant sur la cible.
Даниил Пронин
J'aimerais également quelques conseils sur la façon de fermer un tel menu lorsque l'utilisateur touche ailleurs ou commence à faire défiler. Je suppose qu'il pourrait y avoir un autre touchstartauditeur sur le corps (ou similaire) mais je me suis demandé s'il y avait un meilleur moyen. Merci!
Darryl Young
2
Est-il possible d'ignorer un geste de défilement? Cela fait exactement ce dont j'ai besoin, mais maintenant je ne peux pas faire défiler le contenu qui a cet effet.
alsobubbly le
1
J'ai supprimé le preventDefault car je veux que le défilement soit disponible mais merci pour les conseils sur touchstart et touchend! Dieu envoie ceci est la seule réponse qui fonctionne de manière fiable sur tous les navigateurs.
Petraeus
54

Tout ce que vous avez à faire est de lier touchstart à un parent. Quelque chose comme ça fonctionnera:

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

Vous n'avez rien à faire dans la fonction, laissez-la vide. Ce sera suffisant pour obtenir des survols au toucher, donc un toucher se comporte plus comme: survol et moins comme: actif. La magie iOS.

Razvan Cercelaru
la source
2
Cette solution permet-elle d'exécuter le lien au deuxième robinet?
samuelkobe
1
Non, cela déclenche encore le clic au premier contact. Je viens de le tester.
Jack
Solution géniale! Rapide et facile.
Hunter Turner
41

Essaye ça:

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

Et dans votre CSS:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

Avec ce code, vous n'avez pas besoin d'une classe .hover supplémentaire!

Anselme
la source
1
cela fonctionne pour moi .. d'autres choses qui pourraient aider cela encore plus sont au lieu d'ajouter du javascript, faites ceci: <body ontouchstart = ""> puis ajoutez une petite minuterie dans le css: active event: tr: active {background-color : # 118aab; transition: toutes les .2s linéaires; -webkit-tap-highlight-color: rgba (0,0,0,0); -webkit-user-select: aucun; -webkit-touch-callout: none}
Kozy
A travaillé pour moi. Merci ... La seule chose - il traîne un peu ... je veux dire - après avoir appuyé sur, il ne se charge pas instantanément.
Roman Losev
Utilisez fastclick.js pour supprimer le décalage
Anselm
25

Pour répondre à votre question principale: "Comment simuler un survol avec une touche dans les navigateurs tactiles?"

Permettez simplement de «cliquer» sur l'élément (en touchant l'écran), puis déclenchez l' hoverévénement à l'aide de JavaScript.

var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

Cela devrait fonctionner, tant qu'il y a un hoverévénement sur votre appareil (même s'il n'est normalement pas utilisé).

Mise à jour: je viens de tester cette technique sur mon iPhone et cela semble fonctionner correctement. Essayez-le ici: http://jsfiddle.net/mathias/YS7ft/show/light/

Si vous souhaitez utiliser un `` long contact '' pour déclencher le survol, vous pouvez utiliser l'extrait de code ci-dessus comme point de départ et vous amuser avec les minuteries et autres choses;)

Mathias Bynens
la source
Je ne savais pas à propos de ce bit onhover.call (p), c'est plutôt cool. Il n'y a pas de vol stationnaire cependant ...
Rich Bradshaw
Avez-vous testé cela avec plusieurs éléments et un toucher long? Cela signifie que l'utilisateur déplace son doigt sur l'écran et affiche un style de survol pour chaque élément?
Marcus
On dirait que si vous utilisez déjà jQuery, vous pourrez peut-être simplement appeler l'événement hover avec jQuery lui-même, par exemple: jQuery.hover (); Pas sûr que l'API le prenne en charge, cependant.
Kzqai
Oups, c'était un commentaire plus applicable au post ci-dessus qui utilise en fait jQuery, désolé.
Kzqai
Avez-vous besoin d'un script spécial ou d'appeler la fonction? Cela fonctionne pour moi mais sur une seule page et je ne sais pas quelle est la différence.
Richard Young
13

Solution encore améliorée

J'ai d'abord adopté l'approche de Rich Bradshaw , mais des problèmes ont commencé à apparaître. En exécutant e.preventDefault () sur l'événement 'touchstart' , la page ne défile plus et, ni l' appui long ne peut déclencher le menu d'options ni le zoom double-clic ne peut terminer l'exécution.

Une solution pourrait être de découvrir quel événement est appelé et juste e.preventDefault () dans le dernier, «touchend» . Puisque le ` ` touchmove '' de scroll vient avant `` touchend '', il reste comme par défaut, et le `` clic '' est également empêché car il vient après des mots dans la chaîne d'événements appliquée au mobile, comme ceci:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

Mais alors, lorsque le menu d'options apparaît, il ne déclenche plus le «touchend» responsable du basculement hors de la classe, et la prochaine fois que le comportement de survol sera l'inverse, totalement mélangé.

Une solution serait alors, encore une fois, de découvrir conditionnellement dans quel événement nous sommes, ou simplement d'en faire des séparés, et d'utiliser respectivement addClass () et removeClass () sur 'touchstart' et 'touchend' , en s'assurant qu'il commence et se termine toujours par respectivement ajouter et supprimer au lieu de décider conditionnellement. Pour finir, nous lierons également le rappel de suppression au type d'événement 'focusout' , en restant responsable de la suppression de la classe de survol de tout lien qui pourrait rester active et ne plus jamais être revisitée, comme ceci:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

Attention: certains bugs se produisent encore dans les deux solutions précédentes et, pensez également (non testé), le zoom double-clic échoue toujours aussi.

Solution Javascript ordonnée et, espérons-le, sans bogue (pas :))

Maintenant , pour une deuxième approche, plus propre, plus ordonnée et réactive, en utilisant simplement javascript (pas de mélange entre la classe .hover et pseudo: hover) et à partir de laquelle vous pouvez appeler directement votre comportement ajax sur l' événement `` clic '' universel (mobile et bureau) , J'ai trouvé une question assez bien répondue à partir de laquelle j'ai finalement compris comment je pouvais mélanger des événements tactiles et souris ensemble sans que plusieurs rappels d'événements ne changent inévitablement les uns des autres dans la chaîne d'événements. Voici comment:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});
Alexandre Rosário
la source
3

L' hovereffet souris ne peut pas être implémenté dans le périphérique tactile. Quand je suis apparu avec la même situation, safari iosje l'ai utilisé :activeen CSS pour faire effet.

c'est à dire.

p:active {
  color:red;
}

Dans mon cas, cela fonctionne. Peut-être que c'est aussi le cas qui peut être utilisé sans utiliser javascript. Essayez juste.

sijo vijayan
la source
2

Ajoutez ce code, puis définissez la classe "tapHover" sur les éléments que vous souhaitez travailler de cette façon. La première fois que vous appuyez sur un élément, il gagnera la pseudo-classe ": hover" et la classe "tapped". L'événement de clic sera empêché. Deuxième fois que vous appuyez sur le même élément - l'événement de clic sera déclenché.

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
	float:		left;
	
	display:	inline-block;
	margin:		50px 0 0 50px;
	width:		100px;
	height:		100px;
	overflow:	hidden;
	
	font-size:	20px;
	
	border:		solid 1px black;
}
.box.tapHover {
	background:	yellow;
}
.box.tapped {
	border:		solid 3px red;
}
.box:hover {
	background:	red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>

l00k
la source
1

Sans JS spécifique à l'appareil (ou plutôt au navigateur), je suis presque sûr que vous n'avez pas de chance.

Edit: je pensais que vous vouliez éviter cela jusqu'à ce que je relise votre question. Dans le cas de Mobile Safari, vous pouvez vous inscrire pour obtenir tous les événements tactiles similaires à ce que vous pouvez faire avec des UIView-s natifs. Impossible de trouver la documentation pour le moment, j'essaierai de le faire.

Joonas Trussmann
la source
1

Une façon de le faire serait de faire l'effet de survol lorsque le toucher commence, puis de supprimer l'effet de survol lorsque le toucher se déplace ou se termine.

C'est ce que Apple a à dire sur la manipulation tactile en général, puisque vous parlez de l'iPhone.

dessiné
la source
Le contenu du lien est-il toujours pertinent maintenant?
Flavien Volken
1

Mon goût personnel est également d'attribuer les :hoverstyles à l' :focusétat, comme:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

Puis avec le HTML suivant:

<p id="some-p-tag" tabindex="-1">WOOOO</p>

Et le JavaScript suivant:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});
pimbrouwers
la source
1

Résolu 2019 - Survolez le toucher

Il semble maintenant préférable d'éviter d'utiliser le survol avec iOS ou Touch en général. Le code ci-dessous applique votre css tant que le toucher est maintenu, et sans autres menus volants iOS. Faites ceci;

  1. Jquery add: $ ("p"). On ("touchstart", function (e) {$ (this) .focus (); e.preventDefault ();});

  2. CSS: remplacez p: hover par p: focus et ajoutez p: active

Options;

  • remplacer le sélecteur jquery p par n'importe quelle classe, etc.

  • pour que l'effet reste, gardez p: hover aussi, et ajoutez body {cursor: ponter;} de sorte qu'un tap n'importe où le termine

  • essayez les événements click & mouseover ainsi que touchstart dans le même code (mais non testé)

  • supprimer e.preventDefault (); pour permettre aux utilisateurs d'utiliser les menus déroulants ios, par exemple copier

Remarques

  • testé uniquement pour les éléments de texte, ios peut traiter les entrées, etc. différemment

  • testé uniquement sur l'iphone XR ios 12.1.12 et l'ipad 3 ios 9.3.5, en utilisant Safari ou Chrome.

Darren
la source
0

Un mélange de Javascript natif et de jQuery:

var gFireEvent = function (oElem,sEvent) 
{
 try {
 if( typeof sEvent == 'string' && o.isDOM( oElem ))
 {
  var b = !!(document.createEvent),
     evt = b?document.createEvent("HTMLEvents"):document.createEventObject();
  if( b )    
  {  evt.initEvent(sEvent, true, true ); 
    return !oElem.dispatchEvent(evt);
  }
  return oElem.fireEvent('on'+sEvent,evt);
 }
 } catch(e) {}
 return false;
};


// Next you can do is (bIsMob etc you have to determine yourself):

   if( <<< bIsMob || bIsTab || bisTouch >>> )
   {
     $(document).on('mousedown', function(e)
     {
       gFireEvent(e.target,'mouseover' );
     }).on('mouseup', function(e)
     {
       gFireEvent(e.target,'mouseout' );
     });
   }
Codebeat
la source
1
désolé d'être pédant mais jquery est javacript
matpol
2
@matpol: jQuery est javascript mais javascript n'est pas jQuery. Spécial pour vous, j'ajoute le mot «pur», pur javascript. Satisfait monsieur?
Codebeat
vous ne pouvez pas utiliser jquery sans utiliser de javascript «pur». Je fais juste le point parce que certaines personnes qui fréquentent SO ne semblent pas le savoir.
matpol
5
Un mélange de Javascript natif et de jQuery, satisfait?
Codebeat
0

La solution la plus simple que j'ai trouvée: j'avais des balises <span> avec: hover css rules in them. J'ai changé pour <a href = "javascript: void (0)"> et voilà. Les styles de survol dans iOS ont commencé à fonctionner.

Becario Senior
la source
0

L'utilisation peut également utiliser CSS, ajouter le focus et actif (pour IE7 et moins) au lien caché. Exemple de menu ul dans un div avec menu de classe:

.menu ul ul {display:none; position:absolute; left:100%; top:0; padding:0;}
.menu ul ul ul {display:none; position:absolute; top:0; left:100%;}
.menu ul ul a, .menu ul ul a:focus, .menu ul ul a:active { width:170px; padding:4px 4%; background:#e77776; color:#fff; margin-left:-15px; }
.menu ul li:hover > ul { display:block; }
.menu ul li ul li {margin:0;}

Il est tard et non testé, devrait fonctionner ;-)

Mark Groen
la source