美文网首页
移动页面点击穿透问题解决方案

移动页面点击穿透问题解决方案

作者: 程序员之路 | 来源:发表于2017-09-28 20:48 被阅读0次

    移动页面点击穿透问题解决方案

    一.click与300ms延迟

    移动浏览器提供一个特殊的功能:双击(double tap)放大

    300ms的延迟就来自这里,用户碰触页面之后,需要等待一段时间来判断是不是双击(double tap)动作,而不是立即响应单击(click),等待的这段时间大约是300ms。之前有过简单介绍:黯羽轻扬:HTML5触摸事件

    移动事件提供了touchstart、touchmove、touchend却没有提供tap支持,主流框架(库)都是手动实现了自定义tap事件,以求消除300ms延迟,提高页面响应速度。对于简单的页面,可以把touchstart或者touchend当作tap来用,但存在一些问题,比如手指接触目标元素,按住不放,慢慢移出响应区域,会触发touchstart事件执行对应的事件处理器(本不应该触发),touchend事件也存在类似的问题。

    此外,使用原生touch事件也存在点击穿透的问题,因为click是在touch系列事件发生后大约300ms才触发的,混用touch和click肯定会导致点透问题,下面详细介绍:

    二.点击穿透问题

    点击穿透现象有3种:

    点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件

    蒙层的关闭按钮绑定的是touch事件,而按钮下面元素绑定的是click事件,touch事件触发之后,蒙层消失了,300ms后这个点的click事件fire,event的target自然就是按钮下面的元素,因为按钮跟蒙层一起消失了

    跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转

    因为a标签跳转默认是click事件触发,所以原理和上面的完全相同

    另一种跨页面点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了

    和蒙层的道理一样,js控制页面跳转的逻辑如果是绑定在touch事件上的,而且新页面中对应位置的元素绑定的是click事件,而且页面在300ms内完成了跳转,三个条件同时满足,就出现这种情况了

    非要细分的话还有第四种,不过概率很低,就是新页面中对应位置元素恰好是a标签,然后就发生连续跳转了。。。诸如此类的,都是点击穿透问题

    三.解决方案

    问题已经很明了了,有很多解决方案,但思路不外乎2种:

    不要混用touch和click

    既然touch之后300ms会触发click,只用touch或者只用click就自然不会存在问题了

    吃掉(或者说是消费掉)touch之后的click

    依旧用tap,只是在可能发生点击穿透的情形做额外的处理,拿个东西来挡住、或者tap后延迟350毫秒再隐藏mask、pointer-events、在下面元素的事件处理器里做检测(配合全局flag)等等,能吃掉就行

    详细解决方案:

    只用touch

    最简单的解决方案,完美解决点击穿透问题

    把页面内所有click全部换成touch事件(touchstart、’touchend’、’tap’),需要特别注意a标签,a标签的href也是click,需要去掉换成js控制的跳转,或者直接改成span + tap控制跳转。如果要求不高,不在乎滑走或者滑进来触发事件的话,span + touchend就可以了,毕竟tap需要引入第三方库

    不用a标签其实没什么,移动app开发不用考虑SEO,即便用了a标签,一般也会去掉所有默认样式,不如直接用span

    只用click

    下下策,因为会带来300ms延迟,页面内任何一个自定义交互都将增加300毫秒延迟,想想都慢

    不用touch就不会存在touch之后300ms触发click的问题,如果交互性要求不高可以这么做,强烈不推荐,快一点总是好的

    拿个东西来挡住

    比较笨的方法,千万不要用

    叶小钗的“菊花”大法,更多信息请查看【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

    tap后延迟350ms再隐藏mask

    改动最小,缺点是隐藏mask变慢了,350ms还是能感觉到慢的

    只需要针对mask做处理就行,改动非常小,如果要求不高的话,用这个比较省力

    pointer-events

    比较麻烦且有缺陷,不建议使用

    mask隐藏后,给按钮下面元素添上pointer-events: none;样式,让click穿过去,350ms后去掉这个样式,恢复响应

    缺陷是mask消失后的的350ms内,用户可以看到按钮下面的元素点着没反应,如果用户手速很快的话一定会发现

    在下面元素的事件处理器里做检测(配合全局flag)

    比较麻烦,不建议使用

    全局flag记录按钮点击的位置(坐标点),在下面元素的事件处理器里判断event的坐标点,如果相同则是那个可恶的click,拒绝响应

    上面说的只是想法,没测试过,实在不行就用记录时间戳判断,等待350ms,这样就和pointer-events差不多

    fastclick

    好用的解决方案,不介意多加载几KB的话,不建议使用,因为有人遇到了bug,更多信息请查看:Fastclick 导致click事件触发两次的问题

    首先引入fastclick库,再把页面内所有touch事件都换成click,其实稍微有点麻烦,建议引入这几KB就为了解决点透问题不值得,不如用第一种方法呢

    -----------------------------------------------------------------------------------------------------------------

    最近在做微信wap 站时遇到 这样一个现象,给页面加一个遮罩层,点击遮罩层,遮罩层消失,如果页面内input 框,input 框会自动获取焦点,上网搜了一下,原来是“click点透事件”,

    1. 什么是click点透

    核心代码:

    链接

    弹出

    弹出层

    关闭

    var oPop =document.getElementById('popupLayer');

    var oUn = document.getElementById('underLayer');

    var oOpen = document.getElementById('openPopup');

    oPop.addEventListener('touchend', function(e){

    this.style.display='none';

    });

    oUn.addEventListener('click',

    function(){

    alert('1');

    } );

    点击弹出层,touch事件首先被触发,弹出层和遮罩就被隐藏了。touchend后继续等待300ms发现没有其他行为了,则继续触发click,由于这时弹出层已经消失,所以当前click事件的target就在底层元素上,于是就alert内容。整个事件触发过程为 touchend ->  click。

    而由于click事件的滞后性(300ms),在这300ms内上层元素隐藏或消失了,下层同样位置的DOM元素触发了click事件(如果是input框则会触发focus事件,如果是链接则会进行页面跳转,或是 select / radio / checkbox都会被触发),看起来就像点击的target“穿透”到下层去了。这就是点透现象

    2.解决方法:

    a、阻止默认事件  e.preventDefault() 给touchend事件加上  e.preventDefault()

    oPop.addEventListener('touchend', function(e){

    this.style.display='none';

    e.preventDefault();

    });

    b、利用css3属性 pointer-events

    取值auto|none

    当取值为auto 时,效果和没有定义 pointer-events 属性相同,鼠标不会穿透当前层。

    当取值为none 时,元素不再是鼠标事件的目标,鼠标不再监听当前层而去监听下面的层中的元素。但是如果它的子元素设置了pointer-events为其它值,比如auto,鼠标还是会监听这个子元素的。

    详细代码:

    oPop.addEventListener('touchend', function(e){

    this.style.display='none';

    oUn.style.pointerEvents='none';

    setTimeout(function(){

    oUn.style.pointerEvents='auto';

    }, 400);

    });

    c. 遮挡

    由于 click 事件的滞后性,在这段时间内原来点击的元素消失了,于是便“穿透”了。因此我们顺着这个思路就想到,可以给元素的消失做一个fade效果,类似jQuery里的fadeOut,并设置动画duration大于300ms,这样当延迟的 click 触发时,就不会“穿透”到下方的元素了。

    同样的道理,不用延时动画,我们还可以在触摸位置放一个透明的元素,这样当上层元素消失而延迟的click来到时,它点击到的是那个透明的元素,也不会“穿透”到底下。在一定的timeout后再将生成的透明元素隐藏。代码如下:

    oPop.addEventListener('touchend', function(e){

    oBg.style.display ='block';

    this.style.display='none';

    // 解决方法三

    setTimeout(function(){

    oBg.style.display='none'

    }, 400);

    });

    相关文章

      网友评论

          本文标题:移动页面点击穿透问题解决方案

          本文链接:https://www.haomeiwen.com/subject/gaivextx.html