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

问题来源

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

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

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

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

  3. 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绑定的函数。

一般情况的event的各种属性

基于这个猜想,我开始翻阅mozillaW3C的文档来了解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的属性

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

先来看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对象了。到这里大家应该就比较明白,这里就是浏览器的事件机制了。

event-flow

这里就应该是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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容