阅读 637
移动端点击穿透(点击延迟)问题

移动端点击穿透(点击延迟)问题

先上一个例子,在div.main中存在2个子元素.left、.right,正常的情况下是左侧的元素left撑满了整个main,但是在点击left后,right的宽度会变成200px出现在右侧。

left中绑定了一个touchstart事件,和click的事件,回调函数都是一样,给right加上一个active的class,使right的宽度变为200px,代码如下:

1.png

初始的情况下,屏幕上显示一个全屏宽,高500px的绿色,在正常pc的浏览器中,由于没有touchstart事件,所以在点击left元素的左侧时,控制台会只会打印出left click和时间戳,然后right元素就会在右边出现,且right的点击事件并没有被触发。

2.png

接着,我们点开chrome中的把调试模式切换成移动端,刷新网页,重新点击left元素的左侧时,完全不一样的结果呈现在了控制台中:

3.png

left的touchstart事件,和right的click事件被触发了,两次event触发的位置clientX,clientY相同,且中间相隔了差不多300ms,而left的click事件并没有被触发。因为在touchstart的回调触发时,right已经出现在了右侧,接着相同位置上的click被触发,此时的click事件作用在了right上,触发了right的click回调。

这就是我在项目中实际遇到的问题,项目由vue搭建,本来是跑在pc端的,然后因为业务需求,需要跑在pad端,用cordova套了层壳以后,运行是没什么问题,但是300ms的点击延迟却显得十分的刺眼。考虑代码的修改成本,还有同时适配pc和pad端的需求,我选择引入了vue-hammer来解决点击延迟的问题。

但是计划赶不上变化,本来的右边right是固定的,但是由于移到pad端,需要进行产品设计以及ui交互的修改,pad屏幕小,右侧的right一直显得有些占用空间,需求就变成了,点击左侧left中的元素后,才会弹出right,然后right中有一系列的操作按钮,改完之后就发现悲剧了,点击left之后,直接right中的操作按钮被click了。

我反应还算挺快的,一下子就想到了是移动端点击穿透的问题,愣了一会,想了想。接着,本着能cv大法就不自己想的原则,开始查起了资料,一查发现遇到相同问题的人还挺多,虽然遇到问题的情况不大一样,有页面跳转,有遮罩层点击等等,但解决问题的方法都是类似的。大概有以下这些方法:

1.增加过渡效果

在右侧的right弹出的时候加上transition。听起来不错,有动画感,代码改动量小,直接就给他安排上了,在chrome里看到时候一切正常,长舒一口气,打包个apk运行到真机上。 不得不跟大家提个醒,不是所有的pad都是ipad,很多安卓pad的性能跟手机和我们的pc是天差地别(除非你能说服客户舍得花这个钱),而且混合开发的性能也不如原生开发,直接导致右侧弹出的right的过渡动画效果简直跟便秘似的,一步一卡,三步一顿,不忍直视,方案一直接倒闭。

2.给meta增加属性,加快click点击事件的触发

乍一听还挺不错的,立马加上,<meta name="viewport"content="width=device-width,initial-scale=1.0">。然后试了一下点击。

4.png

发现延迟大大减少了,从300ms减到了100ms左右,应该是一个完全能接受的结果,打个包跑到ipad里。发现,完全没有用,没有起到任何的效果,该延迟的还是延迟,方案二也倒闭了。

3.Event. preventDefault()阻止默认事件

在touchstart的回调函数中加入e.preventDefault();,阻止后续的click事件触发。

5.png 跑到页面上,结果还是挺不错的,right中的click事件如愿地没有触发,

6.png 目的是达到了,但是由于要同时应用于pc端和pad端,必须得同时绑定click事件和touchstart事件

7.png 简单的测试之后,发现是没有问题,但是多了几下,问题就出现了,首先如果这样写,肯定就不支持双击事件了(小问题,不行就跟产品battle下,晓之以情,动之以理,让他把双击功能砍了),但是问题却不只这个,出现了一个奇怪的状态:

8.png 在正常的区域内是没问题的,但是在chrome移动端模式的时候,出现的是一个灰色的小圆点,小圆点点击在临界区域的时候,就出问题了,元素正常被选中了,但是e. preventDefault()并没有生效,按钮还是被触发了。方案三,宣告倒闭。

4.使用css3的pointer-event

这个属性倒是经常使用,防止不必要的元素被点击,可以套上一个pointer-event:none来阻止任何事件,但是怎么使用在这里,整了半天也没整明白,方案四还没实践直接就倒闭了

至此,网上能找到的方案全部扑街,时间倒是过去了很久,啥也没倒腾出来。休息了会,跳了个绳,回到位置上理了理思路,还是得从最简单的思路想起,怎么阻止这个click事件,可能是跳绳带来了灵感(适时的放松和运动确实挺有必要的)。

虽然click和touch事件的触发事件不一样,但是它们触发的位置都是同一个,我只需要在接下来的300ms内禁止那个位置的click事件触发就可以。

和正常绑定事件一样,hammer也会返回给我一个event,我可以获取到事件的触发位置,接着,为了从最外层就禁止掉click事件,而不能等click事件冒泡上来,就要用到一直以来很少被用到的事件捕获了。

在选择元素绑定的事件触发后,我在document层面监听click事件,并设置一个定时器,在350ms以后把这个事件监听取消,然后使用e.stopPropagation();阻止事件往下传递。(e.stopPropagation();不止能阻止冒泡,同样也能阻止事件捕获。)然后只需要判断是否在区域内,在区域内的话,就直接拦截。

9.png

10.png

写完之后,放到刚刚的demo里尝试一下,结果如下:

可以算是比较成功地拦截了click事件的触发。

11.png

运用到正式的项目中也没什么问题,仔细想了想对实际的用户操作和交互基本没有影响,客户也不可能在350ms内立刻点击右侧的操作按钮,至此,这个问题也就告一段落了。

总结:

1.遇到问题可以先想一想,再去网上查资料,适合别人的不一定适合自己的项目,自己的项目只有自己更了解需求和业务逻辑

2.问题想不出的时候,确实应该适当地放松一下,蒙头思考不一定能取得好的效果

3.希望大家能关注我的公众号,懒狗小前端,谢谢大家

文章分类
前端
文章标签