J'ai quelques sites Web que j'ai construits il y a longtemps, qui utilisent des événements de souris jquery ... Je viens de recevoir un ipad et j'ai remarqué que tous les événements de souris survolés sont traduits en clics ... donc par exemple je dois faire deux clics au lieu d'un .. (le premier vol stationnaire, que le clic réel)
existe-t-il une solution de contournement prête à résoudre ce problème? peut-être une commande jquery que je devrais utiliser à la place de mouseover/out etc .. merci!
Je n'ai pas encore testé cela complètement, mais comme iOS déclenche des événements tactiles, cela pourrait fonctionner si vous êtes dans un paramètre jQuery.
$('a').on('click touchend', function(e) {
var el = $(this);
var link = el.attr('href');
window.location = link;
});
L'idée est que Mobile WebKit déclenche un événement touchend
à la fin d'un tap, nous l'écoutons puis redirige le navigateur dès qu'un événement touchend
a été déclenché sur un lien.
Votre question n'est pas tout à fait claire, mais si vous voulez simplement éliminer le double clic tout en conservant l'effet de survol de la souris, mon conseil est le suivant:
touchstart
et mouseenter
.mouseleave
, touchmove
et click
.Afin de simuler une souris, des navigateurs tels que Webkit mobile déclenchent les événements suivants lorsqu'un utilisateur touche et relâche un doigt sur un écran tactile (comme un iPad) (source: Touch And Mouse sur html5rocks.com):
touchstart
touchmove
touchend
mouseover
mouseenter
mouseover
, mouseenter
ou mousemove
modifie le contenu de la page, les événements suivants ne sont jamais déclenchés.mousemove
mousedown
mouseup
click
Il ne semble pas possible d'indiquer simplement au navigateur Web de sauter les événements de la souris.
Pire, si un événement mouseover modifie le contenu de la page, l'événement click ne sera jamais déclenché, comme expliqué dans le document Guide du contenu Web Safari - Gestion des événements , en particulier la figure 6.4 dans Événements à un doigt. Ce qui est exactement un "changement de contenu", dépendra du navigateur et de la version. J'ai constaté que pour iOS 7.0, un changement de couleur d'arrière-plan n'est pas (ou plus?) Un changement de contenu.
Récapituler:
touchstart
et mouseenter
.mouseleave
, touchmove
et click
.Notez qu'il n'y a aucune action sur touchend
!
Cela fonctionne clairement pour les événements de 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 garantit qu'il est supprimé si l'utilisateur appuie sur le bouton de retour dans le navigateur Web.
Cela fonctionne également pour les événements tactiles: sur touchstart
, l'effet de survol est ajouté. Il est '' 'pas' '' supprimé de touchend
. Il est ajouté à nouveau sur mouseenter
et, comme cela ne provoque aucune modification du contenu (il a déjà été ajouté), l'événement click
est également déclenché et le lien est suivi sans que l'utilisateur n'ait à cliquer à nouveau!
Le retard de 300 ms d’un navigateur entre un événement touchstart
et click
est en réalité bien utilisé car l’effet de survol sera affiché pendant cette courte période.
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 événement mouseleave
n'est déclenché et l'effet de survol reste en place. Heureusement, cela peut être facilement résolu en supprimant l'effet de survol sur touchmove
.
C'est tout!
Notez qu'il est possible de supprimer le délai de 300 ms, par exemple à l'aide de la bibliothèque FastClick , mais cette question est hors de portée.
J'ai trouvé les problèmes suivants avec les alternatives suivantes:
touchend
: Cela ne suivra pas correctement le lien, même si l'utilisateur ne souhaitait que faire défiler ou zoomer, sans avoir l'intention de cliquer sur le lien.touchend
qui est utilisée comme condition if dans les événements de souris suivants pour empêcher les changements d'état à ce moment précis. La variable est réinitialisée dans l'événement click. 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 une touchend
est déclenchée pour une autre raison et qu'aucun événement de clic n'est déclenché (par exemple, l'utilisateur fait défiler ou zoomé), et essaie par la suite de suivre le lien avec une souris (c'est-à-dire sur un périphérique avec les deux interface).mouseover
ou mousemove
.Voir aussi Problème de double-clic iPad/iPhone et Désactiver les effets de survol sur les navigateurs mobiles .
On dirait qu'il existe une solution CSS après tout. La raison pour laquelle Safari attend une seconde pression est due à l’image (ou aux éléments) d’arrière-plan que vous affectez habituellement à l’événement: hover. S'il n'y en a aucun à afficher, vous n'aurez aucun problème. La solution consiste à cibler la plate-forme iOS avec un fichier CSS secondaire (ou un style dans le cas d'une approche JS) qui remplace: survolez l'arrière-plan pour hériter par exemple et conservez masqué les éléments que vous alliez afficher au passage de la souris:
Voici un exemple CSS et HTML - un bloc de produit avec une étiquette marquée au passage de la souris:
HTML:
<a href="#" class="s"><span class="s-star"></span>Some text here</a>
CSS:
.s {
background: url(some-image.png) no-repeat 0 0;
}
.s:hover {
background: url(some-image-r.png) no-repeat 0 0;
}
.s-star {
background: url(star.png) no-repeat 0 0;
height: 56px;
position: absolute;
width: 72px;
display:none;
}
.s:hover .s-star {
display:block;
}
Solution (CSS secondaire):
/* CSS */
/* Keep hovers the same or hidden */
.s:hover {
background:inherit;
}
.s:hover .s-star {
display:none;
}
Ce qui a fonctionné pour moi, c’est ce que d’autres ont déjà dit ici:
Ne pas afficher/masquer les éléments en survol ou en souris (ce qui est l'événement dans mon cas).
Voici ce que dit Apple ( https://developer.Apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html ):
Un élément cliquable est un lien, un élément de formulaire, une zone de carte graphique ou tout autre élément avec des gestionnaires de déplacement de souris, de souris, de souris ou de clic
Si l'utilisateur appuie sur un élément cliquable, les événements arrivent dans cet ordre: mouseover, déplacer la souris, déplacer la souris, souris, et cliquer. De plus, si le contenu de la page change lors de l'événement mousemove, aucun événement ultérieur de la séquence n'est envoyé. Ce comportement permet à l'utilisateur de saisir le nouveau contenu.
Vous pouvez donc utiliser la solution de @ woop: détecter l'utilisateurAgent, vérifier s'il s'agit d'un périphérique iOS, puis lier l'événement. J'ai fini par utiliser cette technique parce qu'elle convient à mes besoins et qu'il est plus logique de ne pas lier les événements de survol lorsque vous ne le souhaitez pas.
Mais ... si vous ne voulez pas jouer avec userAgents et toujours masquer/afficher des éléments sur hover/mousemove, j'ai découvert que vous pouvez le faire en utilisant du javascript natif, comme ceci:
$("body").on("mouseover", function() {
document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
document.querySelector(".my-selector div").style.display = 'none'; // hide element
});
Cela fonctionnera sur la version Desktop et ne fera rien sur la version mobile.
Et pour un peu plus de compatibilité ...
$("body").on("mouseover", function() {
if (document.getElementsByTagName && document.querySelector) { // check compatibility
document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
document.querySelector(".my-selector div").style.display = 'none'; // hide element
} else {
$(".my-class").show();
$(".my-selector div").hide();
}
});
la solution de cduruk a été assez efficace, mais a causé des problèmes sur quelques parties de mon site. Comme j'utilisais déjà jQuery pour ajouter la classe de survol CSS, la solution la plus simple consistait simplement à ne pas ajouter la classe de survol CSS sur les appareils mobiles (ou plus précisément, à l'ajouter UNIQUEMENT lorsque PAS sur un appareil mobile).
Voici l’idée générale:
var device = navigator.userAgent.toLowerCase();
var ios = device.match(/(iphone|iPod|ipad)/);
if (!(ios)) {
$(".portfolio-style").hover(
function(){
$(this).stop().animate({opacity: 1}, 100);
$(this).addClass("portfolio-red-text");
},
function(){
$(this).stop().animate({opacity: 0.85}, 100);
$(this).removeClass("portfolio-red-text");
}
);
}
* code réduit à des fins d'illustration
Pas besoin de faire trop compliqué.
$('a').on('touchend', function() {
$(this).click();
});
Je pense qu'il serait sage d'essayer mouseenter
au lieu de mouseover
. C'est ce qui est utilisé en interne lors de la liaison à .hover(fn,fn)
et c'est généralement ce que vous voulez.
La réponse de MacFreak m'a été extrêmement utile. Voici un code pratique au cas où cela vous aiderait.
PROBL&EGRAVE;ME- appliquer touchend signifie que chaque fois que vous faites glisser votre doigt sur un élément, celui-ci réagit comme si vous l'aviez appuyé, même si vous vouliez simplement faire défiler l'écran.
Je crée un effet avec jQuery qui soulève une ligne sous certains boutons pour "mettre en surbrillance" le bouton survolé. Je ne veux pas que cela signifie que vous devez appuyer deux fois sur le bouton des appareils tactiles pour suivre le lien.
Voici les boutons:
<a class="menu_button" href="#">
<div class="menu_underline"></div>
</a>
Je veux que la div "menu_underline" passe en fondu au passage de la souris et disparaisse en sortie de la souris. MAIS je veux que les appareils tactiles puissent suivre le lien en un seul clic, pas deux.
SOLUTION- Voici le jQuery pour le faire fonctionner:
//Mouse Enter
$('.menu_button').bind('touchstart mouseenter', function(){
$(this).find(".menu_underline").fadeIn();
});
//Mouse Out
$('.menu_button').bind('mouseleave touchmove click', function(){
$(this).find(".menu_underline").fadeOut();
});
Merci beaucoup pour votre aide sur ce MacFreak.
J'ai eu les problèmes suivants avec les solutions existantes et j'ai trouvé quelque chose qui semble les résoudre tous. Cela suppose que vous visiez quelque chose entre plusieurs navigateurs, et que vous ne vouliez pas renifler un périphérique.
En utilisant seulement touchstart
ou touchend
:
Le déclenchement d'événements mouseover
sur touchstart
et de mouseout
sur touchmove
a des conséquences moins graves, mais interfère avec le comportement habituel du navigateur, par exemple:
touchstart
comme un mouseover
, qui est mouseout
ed sur la prochaine touchstart
. Une façon de voir le contenu de la souris sur Android est donc de toucher la zone d'intérêt et de bouger votre doigt, en faisant légèrement défiler la page. Traiter touchmove
comme mouseout
casse ceci.En théorie, vous pouvez simplement ajouter un indicateur avec touchmove
, mais les iPhones déclenchent le déplacement tactile même s'il n'y a pas de mouvement. En théorie, vous pouvez simplement comparer les événements touchstart
et touchend
pageX
et pageY
_ { mais sur les iPhones, il n'y a pas de touchend
pageX
ou pageY
.
Donc, malheureusement, couvrir toutes les bases est un peu plus compliqué.
$el.on('touchstart', function(e){
$el.data('tstartE', e);
if(event.originalEvent.targetTouches){
// store values, not reference, since touch obj will change
var touch = e.originalEvent.targetTouches[0];
$el.data('tstartT',{ clientX: touch.clientX, clientY: touch.clientY } );
}
});
$el.on('touchmove', function(e){
if(event.originalEvent.targetTouches){
$el.data('tstartM', event.originalEvent.targetTouches[0]);
}
});
$el.on('click touchend', function(e){
var oldE = $el.data('tstartE');
if( oldE && oldE.timeStamp + 1000 < e.timeStamp ) {
$el.data('tstartE',false);
return;
}
if( $el.data('iosTouchM') && $el.data('tstartT') ){
var start = $el.data('tstartT'), end = $el.data('tstartM');
if( start.clientX != end.clientX || start.clientY != end.clientY ){
$el.data('tstartT', false);
$el.data('tstartM', false);
$el.data('tstartE',false);
return;
}
}
$el.data('tstartE',false);
En théorie, il existe (moyens d'obtenir le temps exact utilisé pour une longue presse au lieu d'utiliser simplement 1000 comme approximation, mais en pratique, ce n'est pas si simple et il est préférable d'utiliser un proxy raisonnable .
Je "pense" que vos liens n'ont pas d'événement onmouseover, où 1 tap active onmouseover et le double tap active le lien. mais idk. Je n'ai pas d'iPad ... Je pense que tu dois utiliser les événements de geste/toucher.
Je viens de découvrir que cela fonctionne si vous ajoutez un auditeur vide, ne me demandez pas pourquoi, mais je l'ai testé sur iPhone et iPad avec iOS 9.3.2 et cela a bien fonctionné.
if(/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream){
var elements = document.getElementsByTagName('a');
for(var i = 0; i < elements.length; i++){
elements[i].addEventListener('touchend',function(){});
}
}
Ce court extrait semble fonctionner . Déclenche l'événement click lorsque le lien est exploité
$('a').on('touchstart', function() {
$(this).trigger('click');
});
Juste une amélioration pour éviter la redirection lorsque vous glissez votre doigt sur un lien par erreur.
// tablet "one touch (click)" X "hover" > link redirection
$('a').on('touchmove touchend', function(e) {
// if touchmove>touchend, set the data() for this element to true. then leave touchmove & let touchend fail(data=true) redirection
if (e.type == 'touchmove') {
$.data(this, "touchmove_cancel_redirection", true );
return;
}
// if it's a simple touchend, data() for this element doesn't exist.
if (e.type == 'touchend' && !$.data(this, "touchmove_cancel_redirection")) {
var el = $(this);
var link = el.attr('href');
window.location = link;
}
// if touchmove>touchend, to be redirected on a future simple touchend for this element
$.data(this, "touchmove_cancel_redirection", false );
});
Inspiré de MacFreak, j'ai créé quelque chose qui fonctionne pour moi.
Cette méthode empêche le survol de coller sur un ipad, et empêche le clic d’enregistrer deux clics dans certains cas. En CSS, si vous avez des classes de survol psudo dans votre CSS, remplacez-les par .hover. Par exemple .some-class: passez par
Testez ce code sur un ipad pour voir comment la méthode de survol de css et de js se comporte différemment (dans l’effet de survol uniquement). Le bouton CSS n'a pas d'alerte de clic fantaisie . http://jsfiddle.net/bensontrent/ctgr6stm/
function clicker(id, doStuff) {
id.on('touchstart', function(e) {
id.addClass('hover');
}).on('touchmove', function(e) {
id.removeClass('hover');
}).mouseenter(function(e) {
id.addClass('hover');
}).mouseleave(function(e) {
id.removeClass('hover');
}).click(function(e) {
id.removeClass('hover');
//It's clicked. Do Something
doStuff(id);
});
}
function doStuff(id) {
//Do Stuff
$('#clicked-alert').fadeIn(function() {
$(this).fadeOut();
});
}
clicker($('#unique-id'), doStuff);
button {
display: block;
margin: 20px;
padding: 10px;
-webkit-appearance: none;
touch-action: manipulation;
}
.hover {
background: yellow;
}
.btn:active {
background: red;
}
.cssonly:hover {
background: yellow;
}
.cssonly:active {
background: red;
}
#clicked-alert {
display: none;
}
<button id="unique-id" class="btn">JS Hover for Mobile devices<span id="clicked-alert"> Clicked</span>
</button>
<button class="cssonly">CSS Only Button</button>
<br>This js method prevents hover from sticking on an ipad, and prevents the click registering as two clicks. In CSS, if you have any :hover in your css, change them to .hover For example .some-class:hover to .some-class.hover
J'ai eu le même problème, mais pas sur un appareil tactile. L'événement se déclenche chaque fois que vous cliquez. Il y a quelque chose à propos de la mise en file d'attente d'événements ou autre.
Cependant, ma solution était la suivante: Si le lien est cliqué à nouveau dans X ms, vous renvoyez simplement false.
Pour régler la minuterie par élément, vous pouvez utiliser $.data()
.
Cela peut également résoudre le problème @Ferdy décrit ci-dessus.
Si vous utilisez Modernizr, il est très facile d'utiliser Modernizr.touch, comme mentionné précédemment.
Cependant, je préfère utiliser une combinaison de Modernizr.touch et de test d’agent d’utilisateur, juste pour être en sécurité.
var deviceAgent = navigator.userAgent.toLowerCase();
var isTouchDevice = Modernizr.touch ||
(deviceAgent.match(/(iphone|iPod|ipad)/) ||
deviceAgent.match(/(Android)/) ||
deviceAgent.match(/(iemobile)/) ||
deviceAgent.match(/iphone/i) ||
deviceAgent.match(/ipad/i) ||
deviceAgent.match(/iPod/i) ||
deviceAgent.match(/blackberry/i) ||
deviceAgent.match(/bada/i));
function Tipsy(element, options) {
this.$element = $(element);
this.options = options;
this.enabled = !isTouchDevice;
this.fixTitle();
};
Si vous n'utilisez pas Modernizr, vous pouvez simplement remplacer la fonction Modernizr.touch
ci-dessus par ('ontouchstart' in document.documentElement)
Notez également que le test de l'agent utilisateur iemobile vous donnera une gamme plus étendue d'appareils mobiles Microsoft détectés que Windows Phone.
Vous pouvez utiliser click touchend
,
exemple:
$('a').on('click touchend', function() {
var linkToAffect = $(this);
var linkToAffectHref = linkToAffect.attr('href');
window.location = linkToAffectHref;
});
L'exemple ci-dessus affectera tous les liens sur les appareils tactiles.
Si vous souhaitez cibler uniquement des liens spécifiques, vous pouvez le faire en leur affectant une classe, par exemple:
HTML:
<a href="example.html" class="prevent-extra-click">Prevent extra click on touch device</a>
Jquery:
$('a.prevent-extra-click').on('click touchend', function() {
var linkToAffect = $(this);
var linkToAffectHref = linkToAffect.attr('href');
window.location = linkToAffectHref;
});
À votre santé,
Jeroen
Pour que les liens fonctionnent sans interrompre le défilement tactile, je résous le problème avec l'événement "tap" de jQuery Mobile:
$('a').not('nav.navbar a').on("tap", function () {
var link = $(this).attr('href');
if (typeof link !== 'undefined') {
window.location = link;
}
});
J'ai rencontré une situation similaire dans laquelle des événements étaient liés aux états mouseenter/mouseleave/click d'un élément, mais sur un iPhone, l'utilisateur devait double-cliquer sur l'élément pour déclencher d'abord l'événement mouseenter, puis à nouveau pour déclencher l'événement click. .
J'ai résolu ce problème en utilisant une méthode similaire à celle décrite ci-dessus, mais j'ai utilisé le plugin jQuery $ .browser (pour jQuery 1.9>) et ajouté un événement .trigger à l'événement de liaison mouseenter, comme suit:
// mouseenter event
$('.element').on( "mouseenter", function() {
// insert mouseenter events below
// double click fix for iOS and mouseenter events
if ($.browser.iphone || $.browser.ipad) $(this).trigger('click');
});
// mouseleave event
$('.element').on( "mouseleave", function() {
// insert mouseout events below
});
// onclick event
$('.element').on( "click", function() {
// insert click events below
});
Le .trigger évite d'avoir à double-cliquer sur l'élément en activant le gestionnaire d'événements .click lorsque mouseenter (ou le clic initial) de l'élément est visualisé sur un iPhone ou un iPad. Ce n'est peut-être pas la solution la plus élégante, mais cela fonctionne très bien dans mon cas et utilise un plug-in que j'avais déjà en place, et m'a obligé à ajouter une seule ligne de code pour que mes événements existants fonctionnent avec ces périphériques.
Vous pouvez obtenir le plugin jQuery $ .browser ici: https://github.com/gabceb/jquery-browser-plugin
Cela fonctionne pour moi quand vous avez jquery ui dropdown
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
$('.ui-autocomplete').off('menufocus hover mouseover');
}
Aucune de l'autre réponse ne fonctionne pour moi. Mon application contient de nombreux écouteurs d'événements, des cases à cocher propres et des liens contenant des écouteurs et des liens sans écouteurs.
J'utilise ceci:
var selector = "label, a, button";
var timer;
var startX;
var startY;
$(document).on("click", selector, function (e) {
if ($(this).data("touched") === true) {
e.stopImmediatePropagation();
return false;
}
return;
}).on("touchend", selector, function (e) {
if (Math.abs(startX - e.originalEvent.changedTouches[0].screenX) > 10 || Math.abs(startY - e.originalEvent.changedTouches[0].screenY) > 10)
// user action is not a tap
return;
var $this = $(this);
// Visit: http://stackoverflow.com/questions/1694595/can-i-call-jquery-click-to-follow-an-a-link-if-i-havent-bound-an-event-hand/12801548#12801548
this.click();
// prevents double click
$this.data("touched", true);
if (timer)
clearTimeout(timer);
setTimeout(function () {
$this.data("touched", false);
}, 400);
e.stopImmediatePropagation();
return false;
}).on("touchstart", function (e) {
startX = e.originalEvent.changedTouches[0].screenX;
startY = e.originalEvent.changedTouches[0].screenY;
});
Je suis trop tard, je le sais mais c'est l'une des solutions de contournement la plus facile que j'ai trouvée:
$('body').on('touchstart','*',function(){ //listen to touch
var jQueryElement=$(this);
var element = jQueryElement.get(0); // find tapped HTML element
if(!element.click){
var eventObj = document.createEvent('MouseEvents');
eventObj.initEvent('click',true,true);
element.dispatchEvent(eventObj);
}
});
Cela ne fonctionne pas seulement pour les liens (balises d'ancrage) mais aussi pour les autres éléments. J'espère que cela t'aides.