iPad / iPhone盘旋问题导致用户双击链接

我有一些我以前build立的网站,使用jQuery的鼠标事件…我只是得到了一个ipad,我注意到,所有的鼠标hover事件翻译点击…所以例如,我必须做两个点击,而不是一个..(第一个hover,比实际点击)

有没有解决方法可以解决这个问题? 也许一个jquery命令我shoudl使用,而不是鼠标hover/出等。谢谢!

还没有完全testing,但由于iOS触发事件,这可以工作,假设你在一个jQuery设置。

$('a').on('click touchend', function(e) { var el = $(this); var link = el.attr('href'); window.location = link; }); 

这个想法是,移动WebKit触发一个touchend事件,所以我们听,然后redirect浏览器,一旦链接触发事件。

目前还不完全清楚你的问题是什么,但如果你只是想消除双击,同时保留鼠标的hover效果,我的build议是:

  • touchstarttouchstart上添加hover效果。
  • 移除mouseleavetouchmoveclick上的hover效果。

背景

为了模拟鼠标,如果用户在触摸屏(如iPad)上触摸并释放手指(来源:HTML5rocks.com上的Touch And Mouse ),则Webkit mobile等浏览器会触发以下事件:

  1. touchstart
  2. touchmove
  3. touchend
  4. 300毫秒延迟,浏览器确保这是一个单一的水龙头,而不是双击
  5. mouseover
  6. mouseenter
    • 注意 :如果mouseovermouseentermousemove事件更改页面内容,则永远不会触发以下事件。
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

简单地告诉浏览器跳过鼠标事件似乎是不可能的。

更糟糕的是,如果一个mouseover事件改变了页面内容,那么点击事件就永远不会被触发,正如Safari Web内容指南 – 处理事件所解释的那样,特别是单指事件中的图6.4。 究竟什么是“内容改变”,将取决于浏览器和版本。 我发现对于iOS 7.0,背景颜色的改变不是(或者不再是)一个内容改变。

解决scheme

回顾一下:

  • touchstarttouchstart上添加hover效果。
  • 移除mouseleavetouchmoveclick上的hover效果。

请注意,在touchend上没有任何操作!

这显然适用于鼠标事件: mouseentermouseleave (略有改进的mouseovermouseout版本)被触发,并添加和移除hover。

如果用户实际上click一个链接,hover效果也被删除。 这确保如果用户在Web浏览器中按下后退button,它将被删除。

这也适用于触摸事件:在touchstart添加hover效果。 这是'''不'''删除touchend 。 它会在mouseenter再次mouseenter ,并且由于这不会导致内容更改(已经添加),所以click事件也会被触发,并且链接被执行,而不需要用户再次点击!

touchstart事件和click之间的浏览器之间的300ms延迟实际上是很好的使用,因为在这段时间内会显示hover效果。

如果用户决定取消点击,则手指的移动将如常进行。 通常情况下,这是一个问题,因为没有mouseleave事件被触发,hover效果依然存在。 谢天谢地,这可以通过删除touchmove上的hover效果来touchmove

而已!

请注意,可以删除300ms的延迟,例如使用FastClick库 ,但这个问题超出了范围。

替代scheme

我发现下列替代scheme存在以下问题:

  • 浏览器检测:非常容易出错。 假设设备具有鼠标或触摸,而当触摸显示器增加时,两者的组合将变得越来越普遍。
  • CSS媒体检测:我知道的唯一的仅CSS的解决scheme。 仍然容易出错,并且仍然假定设备具有鼠标或触摸,而两者都是可能的。
  • 模拟touchend的点击事件:即使用户只想滚动或缩放,但不会实际点击链接,这将错误地跟随链接。
  • 使用variables来抑制鼠标事件:这在touchend中设置了一个variables,在随后的鼠标事件中用作if条件来防止在那个时间点的状态变化。 该variables在点击事件中被重置。 如果你不想在触摸界面上产生hover效果,这是一个不错的解决scheme。 不幸的是,如果由于其他原因触发touchend并且没有触发点击事件(例如,用户滚动或缩放),并且随后尝试用鼠标跟随链接(即,在具有鼠标和触摸的设备上),则这不起作用接口)。

进一步阅读

另请参阅iPad / iPhone双击问题以及在移动浏览器上禁用hover效果 。

毕竟似乎有一个CSS解决scheme。 Safari等待第二次触摸的原因是由于您通常在:hover事件上分配的背景图像(或多个元素)。 如果没有显示 – 你不会有任何问题。 解决scheme是针对iOS平台,使用辅助CSS文件(或JS方法中的样式)来覆盖:例如,将背景hover为inheritance,并将要在鼠标上方显示的元素隐藏起来:

以下是一个示例CSS和HTML – 鼠标hover的带有星号的产品块:

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

解决scheme(辅助CSS):

 /* CSS */ /* Keep hovers the same or hidden */ .s:hover { background:inherit; } .s:hover .s-star { display:none; } 

对我来说有效的是这里的其他人已经说过了:

不要显示/隐藏hover或鼠标移动的元素(这是我的情况)。

这是苹果说的( https://developer.apple.com/library/iOS/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html ):

一个可点击的元素是一个链接,表单元素,图像映射区域,或任何其他元素与mousemove,mousedown,mouseup,或onclick处理程序

如果用户点击可点击的元素,事件将按照以下顺序到达:mouseover,mousemove,mousedown,mouseup,然后单击。 另外, 如果页面的内容在mousemove事件中发生变化,则不会发送序列中的后续事件。 这种行为允许用户点击新的内容。

所以,你可以使用@ woop的解决scheme:检测userAgent,检查它是否和iOS设备,然后绑定事件。 我结束了使用这种技术,因为它适合我的需要,它更有意义不绑定hover事件,当你不需要它。

但是,如果你不想混淆userAgents,仍然隐藏/显示hover/ mousemove上的元素,我发现你可以这样做,通过使用原生的JavaScript,如下所示:

 $("body").on("mouseover", function() { document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element document.querySelector(".my-selector div").style.display = 'none'; // hide element }); 

这将在桌面版本上工作,并将在手机版本上不做任何事情。

而多一点兼容性…

 $("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(); } }); 

cduruk的解决scheme是相当有效的,但在我的网站的几个部分造成问题。 因为我已经使用jQuery来添加CSShover类,所以最简单的解决scheme就是不要在移动设备上添加CSShover类(或者更确切地说,只是在移动设备上不添加时)。

这是一般的想法:

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

*代码减less说明的目的

我认为这是明智的尝试mouseenter来代替mouseover 。 这是绑定到.hover(fn,fn)时内部使用的.hover(fn,fn)一般是你想要的。

我“认为”你的链接没有onmouseover事件,其中1点击激活onmouseover和双击激活链接。 但是idk。 我没有iPad。 我想你必须使用手势/触摸事件。

http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariJSRef/SafariJSRef.pdf

我现有的解决scheme存在以下问题,并find了解决所有这些问题的方法。 这假定你的目标是跨浏览器,跨设备,而不想设备嗅探。

这个问题解决了

只需使用touchstarttouchend

  • 当人们试图滚动浏览内容时,导致事件触发,而当他们开始滑动时恰好碰到这个元素 – 意外触发了该操作。
  • 可能导致事件在longpress上被触发,类似于在桌面上点击右键。 例如,如果您的点击事件转到URL X,并且用户在新选项卡中长按X打开X,则用户将感到困惑,以在两个选项卡中findX open。 在某些浏览器(例如iPhone)上,甚至可能会阻止长按菜单的出现。

touchstart上的touchstartmouseout上触发mouseover事件的后果并不那么严重,但会干扰浏览器的常规行为,例如:

  • 长按会触发永不停止的鼠标hover。
  • 许多Android浏览器在touchstart上处理手指的位置,就像在下一个touchstart上进行mouseover touchstart 。 在Android中查看鼠标hover内容的一种方法是触摸感兴趣的区域并摆动手指,稍微滚动页面。 治疗touchmove作为mouseout打破这一点。

解决scheme

理论上,你可以用touchmove添加一个标志,但即使没有移动,iPhone也会触发touchmove。 理论上,你可以比较touchstarttouchend事件pageXpageY 但是在iPhone上,没有touchend pageXpageY

所以不幸的是要覆盖所有的基地,它最终会变得更复杂一点。

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

从理论上讲,有一些方法可以获得longpress的准确时间,而不是仅仅使用1000作为近似值,但实际上并不是那么简单,最好使用合理的代理 。

MacFreak的回答对我非常有帮助。 这里有一些动手的代码,以防止它。

问题 – 每次将手指滑过某个元素时,使用touchend就意味着即使您只是想滚动过去,也会按照您所按的方式进行响应。

我用jQuery创build了一个效果,在一些button下淡出一行以“突出显示”hover的button。 我不希望这意味着你必须在触摸设备上按两次button来跟随链接。

这里是button:

 <a class="menu_button" href="#"> <div class="menu_underline"></div> </a> 

我希望“menu_underline”div在鼠标hover上淡出并在鼠标hover时淡出。 但我希望触摸设备能够按照一个单一的点击链接,而不是两个。

解决scheme – 这是jQuery使其工作:

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

非常感谢您对这个MacFreak的帮助。

我只是发现,如果你添加一个空的侦听器,不要问我为什么,它的工作原理,但我testing了iPhone和iPad的iOS 9.3.2,它工作正常。

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

我有同样的问题,但不是在触摸设备上。 每次点击该事件都会触发。 有事情排队等等。

不过,我的解决scheme是这样的:点击事件(或触摸?)你设置一个计时器。 如果在X毫秒内再次单击链接,则只返回false。

要设置每个元素计时器,您可以使用$.data()

这也可以解决上述的@Ferdy问题。

如果您使用Modernizr,那么使用前面提到的Modernizr.touch非常容易。

不过,我更喜欢使用Modernizr.touch和用户代理testing的组合,只是为了安全起见。

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

如果你不使用Modernizr,你可以简单地用('ontouchstart' in document.documentElement)replace上面的Modernizr.touch函数,

另请注意,testing用户代理iemobile将为您提供比Windows Phone更广泛的检测到的Microsoft移动设备。

你可以使用click touchend

例:

 $('a').on('click touchend', function() { var linkToAffect = $(this); var linkToAffectHref = linkToAffect.attr('href'); window.location = linkToAffectHref; }); 

以上示例会影响触摸设备上的所有链接。

如果你只想定位特定的链接,你可以通过设置一个类来实现,例如:

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

干杯,

吉荣

我碰到类似的情况,我的事件绑定到一个元素的mouseenter / mouseleave / click状态,但是在iPhone上,用户必须双击元素才能首先触发mouseenter事件,然后再触发click事件。

我使用上面类似的方法解决了这个问题,但是我使用了jQuery $ .browser插件(用于jQuery 1.9>),并将一个.trigger事件添加到mouseenter绑定事件中,如下所示:

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

.trigger可以防止在iPhone或iPad上查看元素时通过触发鼠标单击(或初始单击)元素的.click事件处理程序来双击元素。 可能不是最优雅的解决scheme,但它在我的情况下效果很好,并利用我已经有的插件,并要求我添加一行代码,以使我的现有事件在这些设备下工作。

你可以在这里得到jQuery $ .browser插件: https : //github.com/gabceb/jquery-browser-plugin

只是当你在链接上滑动你的手指时,避免redirect的改进。

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

从MacFreak的灵感,我把一些适合我的东西放在一起。

这种js方法可以防止hover在ipad上,并且在某些情况下防止点击注册为两次点击。 在CSS中,如果你有任何:在你的css中hoverpsudo类,将其改为.hover例如.some-class:hover到.some-class.hover

在ipad上testing这段代码,看看css和jshover方法的行为有什么不同(仅在hover效果中)。 CSSbutton没有一个奇特的点击警报。 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 

为了让链接正常工作而不触碰滚动,我用jQuery Mobile的“tap”事件解决了这个问题:

  $('a').not('nav.navbar a').on("tap", function () { var link = $(this).attr('href'); if (typeof link !== 'undefined') { window.location = link; } }); 

我太迟了,我知道但是这是我find的最简单的解决方法之一:

  $('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); } }); 

这不仅适用于链接(定位标签),也适用于其他元素。 希望这可以帮助。

这短片似乎工作。 点击链接时触发点击事件:

  $('a').on('touchstart', function() { $(this).trigger('click'); }); 

没有从其他答案为我工作。 我的应用程序有很多事件监听器,自己的checkbox和链接,没有监听器的监听器和链接。

我使用这个:

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

没有必要使过度复杂。

 $('a').on('touchend', function() { $(this).click(); }); 

你可以像这样检查navigator.userAgent

 if(!navigator.userAgent.match(/iPhone/i) || !navigator.userAgent.match(/iPad/i)) { //bind your mouseovers... } 

但你将不得不检查黑莓,机器人,其他触摸屏设备。 只有在userAgent包含Mozilla,IE,Webkit或Opera的情况下,您也可以绑定鼠标hover,但您仍然需要屏蔽某些设备,因为Droid会将其userAgentstring报告为:

 Mozilla/5.0 (Linux; U; Android 2.0.1; en-us; Droid Build/ESD56) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17 

iPhone的string是相似的。 如果你只是屏幕iPhone,iPod,iPad的,Android和黑莓手机,你可能会得到大多数的掌上电脑,但不是全部。

只需制作排除平板电脑和移动设备的CSS媒体查询,然后将hover在其中即可。 你不需要这个jQuery或JavaScript。

 @media screen and (min-device-width:1024px) { your-element:hover { /* Do whatever here */ } } 

并且一定要把它添加到你的html头部,以确保它是用实际像素而不是分辨率来计算的。

 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />