iPad / iPhone悬停问题导致用户双击链接

时间:2010-06-14 16:21:37

标签: jquery ipad hover mouseover

我有一些我以前建立的网站,使用jquery鼠标事件...我刚刚得到一个ipad,我注意到所有鼠标悬停事件都是通过点击转换...所以例如我必须做两次点击而不是一个..(第一个悬停,而不是实际点击)

是否有解决此问题的解决方法?也许是一个jquery命令我shoudl使用而不是mouseover / out等。 谢谢!

25 个答案:

答案 0 :(得分:196)

没有完全测试过,但是因为iOS触发了触摸事件,所以假设你处于jQuery设置中,这可能会有效。

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

想法是Mobile WebKit在点击结束时触发touchend事件,因此我们会监听该事件,然后在链接上触发touchend事件后立即重定向浏览器。

答案 1 :(得分:36)

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

  • touchstartmouseenter上添加悬停效果。
  • 删除mouseleavetouchmoveclick上的悬停效果。

背景

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

  1. touchstart
  2. touchmove
  3. touchend
  4. 300毫秒延迟,浏览器确保这是一次点击,而不是双击
  5. mouseover
  6. mouseenter
    • 注意:如果mouseovermouseentermousemove事件更改了网页内容,则永远不会触发以下事件。
  7. mousemove
  8. mousedown
  9. mouseup
  10. click
  11. 似乎无法简单地告诉webbrowser跳过鼠标事件。

    更糟糕的是,如果mouseover事件更改了页面内容,则不会触发click事件,如Safari Web Content Guide - Handling Events所述,特别是 One-Finger Events 中的图6.4。究竟什么是“内容更改”,取决于浏览器和版本。我发现对于iOS 7.0,背景颜色的变化不是(或不再是?)内容变化。

    解决方案

    回顾一下:

    • touchstartmouseenter添加悬停效果。
    • 删除mouseleavetouchmoveclick上的悬停效果。

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

    这显然适用于鼠标事件:mouseentermouseleavemouseovermouseout稍微改进的版本)会被触发,并添加和删除悬停。

    如果用户实际click是一个链接,则还会删除悬停效果。这可以确保在用户按下Web浏览器中的后退按钮时将其删除。

    这也适用于触摸事件:在touchstart上添加悬停效果。它在touchend上没有被删除。它会再次添加到mouseenter,因为这不会导致内容更改(已添加),所以click事件也会被触发,并且会跟踪链接,而无需用户再次单击!

    浏览器在touchstart事件和click之间的300毫秒延迟实际上得到了充分利用,因为悬停效果将在这么短的时间内显示。

    如果用户决定取消点击,则手指的移动将正常进行。通常,这是一个问题,因为没有触发mouseleave事件,并且悬停效果仍然存在。值得庆幸的是,通过删除touchmove上的悬停效果,可以轻松解决此问题。

    就是这样!

    请注意,可以删除300毫秒延迟,例如使用FastClick library,但这超出了此问题的范围。

    替代解决方案

    我发现以下替代方案存在以下问题:

    • 浏览器检测:极易出错。假设设备具有鼠标或触摸功能,而当触控显示器增加时,两者的组合将变得越来越常见。
    • CSS媒体检测:我唯一知道的仅限CSS的解决方案。仍然容易出错,并且仍假设设备有鼠标或触摸,但两者都是可能的。
    • 模拟touchend中的点击事件:这将错误地跟踪链接,即使用户只想滚动或缩放,而无意实际点击链接。
    • 使用变量来抑制鼠标事件:这会在touchend中设置一个变量,该变量在后续鼠标事件中用作if条件,以防止在该时间点发生状态更改。该变量在click事件中重置。如果您真的不希望在触摸界面上悬停效果,这是一个不错的解决方案。不幸的是,如果touchend由于其他原因被触发并且没有触发点击事件(例如用户滚动或缩放),并且随后尝试使用鼠标跟踪链接(即在设备上鼠标和触摸界面)。

    进一步阅读

    另请参阅iPad/iPhone double click problemDisable hover effects on mobile browsers

答案 2 :(得分:20)

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

以下是CSS和HTML的示例 - 鼠标悬停时带有加星标签的产品块:

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

解决方案(辅助CSS):

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

答案 3 :(得分:5)

对我来说有用的是其他人已经说过的话:

不要在悬停或鼠标移动时显示/隐藏元素(在我的情况下这是事件)。

这就是Apple所说的(https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html):

  

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

     

如果用户点击可点击元素,则事件按此顺序到达:mouseover,mousemove,mousedown,mouseup和click。此外,如果页面内容在mousemove事件中发生更改,则不会发送序列中的后续事件。此行为允许用户点击新内容。

所以,你可以使用@ woop的解决方案:检测userAgent,检查它是否和iOS设备然后绑定事件。我最终使用这种技术,因为它适合我的需要,更有意义的是当你不想要它时不要绑定悬停事件。

但是......如果你不想弄乱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();
    }
});

答案 4 :(得分:3)

无需过度复杂。

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

答案 5 :(得分:3)

cduruk的解决方案非常有效,但在我网站的某些部分引起了问题。因为我已经使用jQuery来添加CSS悬停类,最简单的解决方案就是不在移动设备上添加CSS悬停类(或者更准确地说,只在不在移动设备上时添加它)。

以下是一般概念:

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

*代码为了说明目的而减少

答案 6 :(得分:2)

我认为用mouseenter代替mouseover是明智之举。它是绑定到.hover(fn,fn)时内部使用的,通常是你想要的。

答案 7 :(得分:1)

我在现有解决方案中遇到了以下问题,并找到了似乎可以解决所有这些问题的问题。这假设您的目标是跨浏览器,跨设备,并且不希望设备嗅探。

解决的问题

仅使用touchstarttouchend

  • 当人们尝试滚动浏览内容时导致事件触发,并且当他们开始滑动时恰好触发此元素 - 意外触发操作。
  • 可能导致事件在longpress 上触发,类似于右键单击桌面。例如,如果您的点击事件转到URL X,并且用户长按以在新选项卡中打开X,则用户将很困惑,在两个选项卡中都打开X.在某些浏览器(例如iPhone)上,它甚至可能会阻止显示长按菜单。

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

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

解决方案

理论上,您可以添加touchend的标记,但即使没有移动,iPhone也会触发触摸。从理论上讲,您只需比较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); 和{{1}} but on iPhones, there's no touchend pageX or pageY

所以很遗憾,为了覆盖所有基础,它最终会变得更加复杂。

{{1}}

理论上,有ways to get the exact time used for a longpress instead of just using 1000 as an approximation, but in practice it's not that simple and it's best to use a reasonable proxy

答案 8 :(得分:1)

我刚刚发现,如果你添加一个空的听众,它不会问我原因,但是我在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(){});
    }
}

答案 9 :(得分:1)

MacFreak的回答对我非常有帮助。这里有一些实用的代码,以防它帮助你。

问题 - 应用 touchend 表示每次在元素上滚动手指时,即使您只是尝试滚动,它也会像按下它一样响应过去。

我正在使用jQuery创建一个效果,它会在某些按钮下淡化一行以“突出显示”悬停的按钮。我不希望这意味着您必须在触摸设备上按两次按钮才能跟踪链接。

以下是按钮:

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

我希望“menu_underline”div在鼠标悬停时淡出并在mouseout上逐渐淡出。但我希望触摸设备只需点击一下即可关注链接。

解决方案 - 这是让它运行的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的帮助。

答案 10 :(得分:1)

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

https://developer.apple.com/documentation/webkitjs

答案 11 :(得分:0)

您可以使用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;
});

干杯,

的Jeroen

答案 12 :(得分:0)

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

我使用与上面类似的方法解决了这个问题,但是我使用了jQuery $ .browser插件(对于jQuery 1.9&gt;)并将一个.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
});

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

您可以在此处获取jQuery $ .browser插件:https://github.com/gabceb/jquery-browser-plugin

答案 13 :(得分:0)

只是在您错误地在链接上滑动手指时避免重定向的改进。

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

答案 14 :(得分:0)

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

此js方法可防止悬停粘在ipad上,并在某些情况下防止点击注册为两次点击。在CSS中,如果您有任何:在您的CSS中悬停psudo类,请将它们更改为.hover例如.some-class:hover to .some-class.hover

在ipad上测试此代码,以了解css和js悬停方法的行为方式(仅在悬停效果中)。 CSS按钮没有花哨的点击提醒。 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

答案 15 :(得分:0)

为了在不破坏触摸滚动的情况下使链接正常工作,我使用jQuery Mobile的“tap”事件解决了这个问题:

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

答案 16 :(得分:0)

如果你使用Modernizr,就可以很容易地使用前面提到的Modernizr.touch。

但是,为了安全起见,我更喜欢使用Modernizr.touch和用户代理测试的组合。

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,只需将上面的Modernizr.touch函数替换为('ontouchstart' in document.documentElement)

即可

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

答案 17 :(得分:0)

这个简短的片段似乎有用。 点击链接时触发点击事件:

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

答案 18 :(得分:0)

我有同样的问题,但没有在触控设备上。每次单击时都会触发该事件。有关事件排队等等的事情。

但是,我的解决方案是这样的: 在点击事件(或触摸?)上设置一个计时器。如果在X ms内再次单击该链接,则只返回false。

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

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

答案 19 :(得分:0)

其他答案都没有对我有用。我的应用程序有很多事件监听器,自己的复选框和具有监听器的链接以及没有监听器的链接。

我用这个:

<a class="linkToModal" onclick="showModal('http://www.example.com/seal/1e047f70-948c-4d40-a47f-0a15c59fc243')">MY SITE SEAL</a>
<div id="modal"><iframe src=""></iframe></div>

答案 20 :(得分:0)

当您有jQuery UI下拉菜单时,这对我有用

if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
      $('.ui-autocomplete').off('menufocus hover mouseover');
}

答案 21 :(得分:0)

避免在css悬停事件内更改“显示”样式。我的“显示:阻止”处于悬停状态。删除ios后,只需轻按一下即可进入lins。顺便说一下,似乎最新的IOS更新修复了此“功能”

答案 22 :(得分:0)

解决双击IPad的最简单方法是在媒体查询 import ... class ItemCardsAdapter(private val myDataset: ArrayList<Item>, private val f: Fragment) : RecyclerView.Adapter<ItemCardsAdapter.MyViewHolder>() { class MyViewHolder(viewItem: View) : RecyclerView.ViewHolder(viewItem){ val itemphoto = viewItem.itemphoto val itemtitle = viewItem.title val itemprice = viewItem.itemprice val itemlocation = viewItem.itemlocation val editpencil = viewItem.editCard } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { // create a new view val viewItem = LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false) // set the view's size, margins, paddings and layout parameters return MyViewHolder(viewItem) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(holder: MyViewHolder, position: Int) { holder.itemtitle.text = myDataset[position].title holder.itemprice.text = myDataset[position].price holder.itemlocation.text = myDataset[position].location if(myDataset[position].photoPath != "") /*Here it stucks on load !?*/ holder.itemphoto.setImageBitmap(decodeSampledBitmapFromResource(myDataset[position].photoPath,256,256)) //holder.View.startAnimation(AnimationUtils.loadLayoutAnimation(this,R.anim.layout_animation)) val bundle = bundleOf("itemAdKey" to myDataset[position].adId) holder.editpencil.setOnClickListener { bundle.putString("nav", "editCard") f.findNavController().navigate(R.id.action_nav_itemlist_to_itemEditFragment, bundle) } holder.itemView.setOnClickListener { f.findNavController().navigate(R.id.action_nav_itemlist_to_nav_itemdetails, bundle) } } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = myDataset.size } 中将css包装起来以实现悬停效果:

@media (pointer: fine)

此媒体查询中包装的CSS仅适用于台式机。

此解决方案的说明在https://css-tricks.com/annoying-mobile-double-tap-link-issue/

答案 23 :(得分:-1)

你可以这样检查navigator.userAgent

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

但你必须检查黑莓,机器人,其他触摸屏设备。您也可以仅在userAgent包含Mozilla,IE,Webkit或Opera时绑定鼠标悬停,但您仍然需要筛选某些设备,因为Droid会将其userAgent字符串报告为:

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的字符串类似。如果你只是为iPhone,iPod,iPad,Android和Blackberry屏幕,你可能会得到大部分掌上电脑,但不是全部。

答案 24 :(得分:-1)

只需制作一个不包含平板电脑和移动设备的CSS媒体查询,然后将鼠标悬停在那里。你并不真的需要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" />