资讯专栏INFORMATION COLUMN

新年第一发--深入不浅出zepto的Tap击穿问题

tuantuan / 3020人阅读

摘要:浏览器自动响应后续处理。浏览器行为部分是猜测,未验证。至于解决方案网上有很多,目前最好的是,不过也会有其他问题,例如在滑动中点击之类的。

问题来源

年前去阿里面试,过程中说道了fastclick解决iPhone机器上300ms点击延迟的问题,然后就被问到了zepto的“点击穿透”的现象以及产生这个具体原因,当时回答的不是很好,主要是没有特别深入的去研究这个原因,只是知道有这个现象和问题,大概怎么解决,面试完了之后有一天突然想起来了,就决定仔细的研究下。

其实有好多文章都写了,内容有很多我就不重复,总结以下几点:

300ms延迟是由于浏览器要判断是单机还是双击造成的延迟处理点击事件

fastclick解决方式用touchstart结合touchmove以及touchend替代click事件

zepto的tap会“击穿”页面是由于既响应了自身的tap(也就是touch事件),又没有拦截掉原来的click事件,导致重复执行了2次事件,在有遮罩弹层的时候就会出现“击穿”效果。如果不太明白的话看这篇文章zepto的击穿

年前探究

当时研究到这里时候我有一个大大的疑问就是为什么click延迟执行之后,遮罩层下面的页面的click事件会被触发,我明明点击的遮罩层的A按钮,为何下面页面的B按钮的事件会执行。按照我最初的想法,应该是继续执行A按钮的事件啊!!!此时我内心是这样的

于是我开始探究这个问题,我搜了下大概的资料,基本都没有讲这个具体原因的,也许是我打开方式不对,反正没有找到,无奈之下,我只能翻看fastclick的源码来看它为何没有出现这个问题,然后看到了sendClick的代码,心里猛然有了一个猜想。

FastClick.prototype.sendClick = function(targetElement, event) {
    var clickEvent, touch;
    // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
    if (document.activeElement && document.activeElement !== targetElement) {
        document.activeElement.blur();
    }
    touch = event.changedTouches[0];
    // Synthesise a click event, with an extra attribute so it can be tracked
    clickEvent = document.createEvent("MouseEvents");
    clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
    clickEvent.forwardedTouchEvent = true;
    targetElement.dispatchEvent(clickEvent);
    };

注意这里的initMouseEvent,当时就在想肯定和mouseEvent执行的原理有关了,到这个阶段算是有了眉目。

接着搞

紧接着,开始过年,过年期间享受了生活,并没有碰代码和文档(好堕落的感觉......),加上我跳槽的空档和折腾,年后稍稍稳定下来了,最近又想起了年前这探究一半的猜想,开始继续搞了起来,顺便收收心,好进入状态。

先说猜想--click事件最开始其实在浏览器当中被捕捉的时候,只有mouseEvent的相关属性,也就是我们平常在console.log(event)的一部分,之后,浏览器才会结合html,js产生我们常说的click时间,接着触发我们使用js绑定的函数。

基于这个猜想,我开始翻阅mozilla和W3C的文档来了解mouseEvent。

翻看文档之后发现mouseEvent果然只有 screenX,screenY,clientX,clientY,ctrlKey,altKey,shiftKey,metaKey,button,buttons,EventTarget?relatedTarget。

其中button和buttons指的是鼠标的按钮类型,就是左键,右键,滚轮这些。用数字代替,0表示左键,1是滚轮,2是右键,其他更多功能键,都是大于2的。

从上面我们能看出来,其实对于mouseEvent而言,它只知道我们在屏幕的哪个位置,做了什么动作(鼠标操作),并不知道是在哪个element上面。这也就是fastclick还原用户点击事件最后做的事情。

clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
// detremineEvenType是fastclick封装返回mouseEvent的type类型,就是click还是mouseDown

初始化一个鼠标事件,然后dispatch这个鼠标事件。浏览器自动响应后续处理。

接着来看click的定义,如下图所示:

click会多了Event.target,而且必须是一个 topmost event target,在mozilla定义有些不太相同,多了currentTarget和type等。

先来看EventTarget的定义:EventTarget is an interface implemented by objects that can receive events and may have listeners for them.

Element, document, and window are the most common event targets, but other objects can be event targets too, for example XMLHttpRequest, AudioNode,AudioContext, and others.

从定义就能看出来了,如果是click事件必须要有一个target来承载这次鼠标事件。一般来说target要么是element要么是document,如果都没有那么就是window对象了。到这里大家应该就比较明白,这里就是浏览器的事件机制了。

这里就应该是initMouseEvent之后,浏览器干的事情,来寻找是否有target来响应此次事件,如果前面一直没有target来响应,最后就会到window上,一般来说我们不会在window上做事件处理,就会没有任何响应,事件结束了。如果碰巧的事,此时有target(一般来说就是element了)来响应,那么就会执行绑定的函数了。

总结下整个流程:用户点击屏幕,300ms之内,浏览器拦截下这个行为,没有去真正触发相关element上绑定的click事件执行函数,而是记录操作相关数据,等待接下来的操作,由于我们使用zepto库绑定了tap事件,事件中有监听touchend触发了,立刻执行相关操作,隐藏了弹层。300ms到了,浏览器认为这次动作是click而不是dbclick,然后init一次mouseEvent在相同的屏幕位置,接着开始事件机制,发现相同位置有一个element绑定了click处理函数,执行这个函数,Over!!!穿透就是这样产生的。PS:浏览器行为部分是猜测,未验证。

至于解决方案:网上有很多,目前最好的是fastclick,不过fastclick也会有其他问题,例如在滑动中点击之类的。另外就是用zepto但是要preventDefault。

Android自己chrome已经解决了,可以用其他方式,官方文档,目前Safari也支持了,不过是在高版本上,相关讨论可以看fastclick的issue

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81557.html

相关文章

  • 新年一发--深入浅出zeptoTap击穿问题

    摘要:浏览器自动响应后续处理。浏览器行为部分是猜测,未验证。至于解决方案网上有很多,目前最好的是,不过也会有其他问题,例如在滑动中点击之类的。 问题来源 年前去阿里面试,过程中说道了fastclick解决iPhone机器上300ms点击延迟的问题,然后就被问到了zepto的点击穿透的现象以及产生这个具体原因,当时回答的不是很好,主要是没有特别深入的去研究这个原因,只是知道有这个现象和问题,大...

    andycall 评论0 收藏0
  • 如何实现swipe、tap、longTap等自定义事件

    摘要:分别存储事件的定时器。事件定时器延时时间存储事件对象滑动方向判断我们根据下图以及对应的代码来理解滑动的时候方向是如何判定的。取消长按,以及取消所有事件取消长按取消所有事件方式都是类似,先调用取消定时器,然后释放对应的变量,等候垃圾回收。 前言 移动端原生支持touchstart、touchmove、touchend等事件,但是在平常业务中我们经常需要使用swipe、tap、double...

    罗志环 评论0 收藏0
  • 移动前端 - 收藏集 - 掘金

    摘要:使用移动设备查看页面时会发现,在微信浏览器中有顶部导航栏有效解决图片使用单位边角缺失的问题前端掘金起因在移动端使用布局时图片也需要用单位。移动端实践前端掘金说起,相信大家并不陌生。 Sticky Footer,完美的绝对底部 - 前端 - 掘金写在前面 做过网页开发的同学想必都遇到过这样尴尬的排版问题:在主体内容不足够多或者未完全加载出来之前,就会导致出现(图一)的这种情况,原因是因为...

    Jochen 评论0 收藏0
  • 有价值前端技术点

    摘要:借着产品层面的功能和视觉升级,我们用对它进行了一次技术重构。前端优化是一个让人技术提升的,希望你也能从这里学到一些东西。年最流行的前端链接我们每周会给多名前端开发者发送新闻邮件。 面试 -- 网络 HTTP 现在面试门槛越来越高,很多开发者对于网络知识这块了解的不是很多,遇到这些面试题会手足无措。本篇文章知识主要集中在 HTTP 这块。文中知识来自 《图解 HTTP》与维基百科,若有错...

    microelec 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<