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议是:
- 在
touchstart
和touchstart
上添加hover效果。 - 移除
mouseleave
,touchmove
和click
上的hover效果。
背景
为了模拟鼠标,如果用户在触摸屏(如iPad)上触摸并释放手指(来源:HTML5rocks.com上的Touch And Mouse ),则Webkit mobile等浏览器会触发以下事件:
-
touchstart
-
touchmove
-
touchend
- 300毫秒延迟,浏览器确保这是一个单一的水龙头,而不是双击
-
mouseover
-
mouseenter
- 注意 :如果
mouseover
,mouseenter
或mousemove
事件更改页面内容,则永远不会触发以下事件。
- 注意 :如果
-
mousemove
-
mousedown
-
mouseup
-
click
简单地告诉浏览器跳过鼠标事件似乎是不可能的。
更糟糕的是,如果一个mouseover事件改变了页面内容,那么点击事件就永远不会被触发,正如Safari Web内容指南 – 处理事件所解释的那样,特别是单指事件中的图6.4。 究竟什么是“内容改变”,将取决于浏览器和版本。 我发现对于iOS 7.0,背景颜色的改变不是(或者不再是)一个内容改变。
解决scheme
回顾一下:
- 在
touchstart
和touchstart
上添加hover效果。 - 移除
mouseleave
,touchmove
和click
上的hover效果。
请注意,在touchend
上没有任何操作!
这显然适用于鼠标事件: mouseenter
和mouseleave
(略有改进的mouseover
和mouseout
版本)被触发,并添加和移除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
并且没有触发点击事件(例如,用户滚动或缩放),并且随后尝试用鼠标跟随链接(即,在具有鼠标和触摸的设备上),则这不起作用接口)。
进一步阅读
- http://jsfiddle.net/macfreek/24Z5M/ 。 在这个沙盒中为自己testing上面的解决scheme。
- http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser 。 同样的答案,多一点背景。
- http://www.html5rocks.com/en/mobile/touchandmouse/ 。 html5rocks.com关于触摸和鼠标的大背景文章。
- https://developer.apple.com/library/ios/DOCUMENTATION/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html 。 Safari Web内容指南 – 处理事件。 特别参见图6.4,该图解释了在
mouseover
或mousemove
事件期间内容更改之后不会触发更多事件。
另请参阅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或鼠标移动的元素(这是我的情况)。
一个可点击的元素是一个链接,表单元素,图像映射区域,或任何其他元素与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。 我想你必须使用手势/触摸事件。
我现有的解决scheme存在以下问题,并find了解决所有这些问题的方法。 这假定你的目标是跨浏览器,跨设备,而不想设备嗅探。
这个问题解决了
只需使用touchstart
或touchend
:
- 当人们试图滚动浏览内容时,导致事件触发,而当他们开始滑动时恰好碰到这个元素 – 意外触发了该操作。
- 可能导致事件在longpress上被触发,类似于在桌面上点击右键。 例如,如果您的点击事件转到URL X,并且用户在新选项卡中长按X打开X,则用户将感到困惑,以在两个选项卡中findX open。 在某些浏览器(例如iPhone)上,甚至可能会阻止长按菜单的出现。
在touchstart
上的touchstart
和mouseout
上触发mouseover
事件的后果并不那么严重,但会干扰浏览器的常规行为,例如:
- 长按会触发永不停止的鼠标hover。
- 许多Android浏览器在
touchstart
上处理手指的位置,就像在下一个touchstart
上进行mouseover
touchstart
。 在Android中查看鼠标hover内容的一种方法是触摸感兴趣的区域并摆动手指,稍微滚动页面。 治疗touchmove
作为mouseout
打破这一点。
解决scheme
理论上,你可以用touchmove
添加一个标志,但即使没有移动,iPhone也会触发touchmove。 理论上,你可以比较touchstart
和touchend
事件pageX
和pageY
但是在iPhone上,没有touchend
pageX
或pageY
。
所以不幸的是要覆盖所有的基地,它最终会变得更复杂一点。
$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" />