若是你关注我应该知道,我最近对PC端页面进行移动适配。在这个过程当中,为了节省用户300ms的时间,同时给予用户更及时的点击反馈(这意味着更好的用户体验),我在尝试使用移动端独有的 touchstart
事件替代传统的 click
事件,这过程当中我遇到了一些小问题,并成功解决了,你能够经过这篇文章查看具体的状况。html
所谓祸不单行,在即将发布上线的时候,我又忽然发现使用 touchstart
事件后,移动设备上出现了另外一个比较诡异的现象:当用户滚动页面后,本来绑定在fixed定位的navbar元素上的 touchstart
事件会时常失效。你能够经过扫描下方二维码,并使用你的Safari浏览器或Chrome浏览器(注意:不是浏览器自带的模拟器)亲自感觉这一奇怪的现象。git
固然,最终我成功解决了这个问题,而且有意思的是,这个问题彷佛并不出自个人代码,而被我归咎因而浏览器的Bug。可是对于这个Bug出现的原理,我也只有一个大概的推测,若是你清楚的知道产生这一现象的缘由,也欢迎你和我分享。github
在本篇文章中,我不但会记录个人解决方案,而且会记录我在遇到这个问题后的debug的过程与分析思路。不过若是你正被这个问题搞得焦头烂额,只想快点摆脱这个问题,你能够直接翻阅到文章底部“解决方案”部分,参考个人解决方案(我真是贴心,对吧? ?)。segmentfault
当在移动设备上使用测试浏览器打开网页并滚动屏幕后,会发现再次点击navbar,navbar元素绑定的 touchstart
事件并无被触发(或偶尔不被触发)。浏览器
我是在无心中发现该问题的,当时观察到的现象是绑定在navbar上的 touchstart
事件有时会被触发,有时会失效。因而我使用Google搜索了“touchstart 事件偶尔失效”的关键字,很遗憾,并无什么靠谱的答案。这说明“并不存在touchstart偶尔失效的问题”。也就是说,我须要找到确切的令 touchstart
响应事件失效的缘由。函数
接下来,我不断的在移动设备上尝试各类操做(双击,滚动屏幕,放大等),并留心移动设备的响应,最终将 touchstart
绑定事件失效的缘由肯定为在“页面滚动以后”。接下来的事很简单,继续Google搜索“touchstart 事件在页面滚动后失效”。观察首屏搜索结果,并点击进去查看,遗憾的是,并无什么合适的信息:测试
接着我抱着试试看的态度切换百度搜索一样的关键词,还不错,已经有了一个类似度很高的搜索结果,可是阅读后发现依然不是我想要的:搜索引擎
是时候上最终的大杀器了,使用英文关键字搜索!如下是我使用了“touch event doesn't respond after page scroll”关键字的Google搜索结果:spa
老样子,仍是没有使人满意的结果。至此我得到了两点信息:操作系统
touch
事件的相关概念上找缘由;至此,debug的第一阶段结束,个人徒劳无功代表了这个问题并不简单,我须要认真对待,接下来我采用了如下两种方法:
思路很清晰对吧,可是我并无在相关文档中找到可能引起此问题的任何灵感。不过好在我经过第一步已经让问题变得很是清晰了,不要灰心,继续思考。
基本上到了这个阶段,debug的过程就进入到经验和直觉领域了,要成功解决这个问题,你有时还须要一点点运气,我在这个过程当中尝试了如下方案:
“无脑试对”:我将在搜索引擎中看到的一些问题的解决方法,逐个试验,但愿有个会管用,我能够得到更多信息去定位问题出现的缘由。这些尝试有:为事件的回调函数添加 e.preventDefault()
方法,替换 touchstart
事件为 touchend
事件或者直接是 click
事件。
很尴尬,这些尝试都没有起到做用,问题依然存在,不过没有关系,我原本也没有对这个简短的尝试抱太大的指望,不过这其实也说明这个奇怪的现象和touch具体的事件类型无关,和touch事件误触发其他事件无关。
目前为止,我已经知道了 touch
事件我使用的方式是正确的,而且没有其他的因素能够干扰点击事件的触发,天然而然的,我开始好奇,浏览器到底有没有检测到我手指的“点击”。这能够经过如下代码获得答案:
window.addEventListener("touchstart", e => { console.log(e.target) })
奇妙的事情发生了,个人navbar竟然再也不出现页面滚动后touch事件失效的问题!可是当我按照相同的思路,将代码替换为下面的代码想要看看返回值时:
var navbar = document.querySelector(".navbar") navbar.addEventListener("touchstart", e => { console.log(e.target) })
问题又出现了,而且当页面滚动后,每当我再次点击navbar,控制台没有任何输出,这意味着浏览器认为我并无点击navbar!
这不科学,可是我已然看到胜利的曙光。当我我将原先绑定在navbar上的 touchstart
事件经过事件的冒泡机制绑定在 window
对象,经过判断 e.target
属性进行事件回调时 -- 问题解决了,页面正常了,整个世界都清净了...
代码以下:
var navbar = document.querySelector(".navbar") window.addEventListener("touchstart", e => { if (e.target === navbar) { // callback } })
扫描二维码查看正确的效果:
到此为止问题被成功解决了吗?并无。
虽然世界清净了的那一刻使人神清气爽,可是这只是需求被实现了,问题并无被解决,我指的是我内心的那个问题:“为何这样就行,而原来那样就不行?”。这个问题相当重要,也但愿大家不要忽略。
你赞成吗?那让咱们继续。
让咱们再回过头分析一下咱们的代码,很明显它已经很是精简了,惟一可能出问题的地方在于咱们给navbar的 fixed
定位。咱们再想一想咱们是怎么“误打误撞”解决这个问题的,navbar检测不到咱们的点击,可是window能够,将这两个线索放在一块儿思考,我得出了一个很值得怀疑的对象:层级。
我试着取消了navbar的 position: fixed;
声明,果真,一切又都正常了。看来这一奇异现象的始做俑者就是这条声明。而我能想到与之相关的因素就是层级,我是指DOM对象的层级。
最终我是这样解释这个奇怪现象产生的缘由:
在页面初始化时,浏览器的DOM树被正确的渲染,也就是说DOM元素间的关系正确,所以 navbar 元素能够准确的捕捉咱们的 touchstart 事件,可是当页面滚动后,浏览器丢失了 navbar 元素的层级关系, touchstart 事件没法经过冒泡被 navbar 元素捕捉,所以咱们绑定的事件没有响应。而当咱们让整个window对象监测 touchstart 事件后,浏览器能够从新正确的计算DOM对象间的关系,navbar 层能够捕捉到冒泡的事件,所以一切就都正常了。
这个解释有说服你吗?其实我内心也没个底,毕竟这只是我基于现象的一种推测。但不管如何,这种奇怪现象的发生,应该被归咎于浏览器,而不是个人代码(哈,松了一口气)。
到这里,这个问题就结束了吗?并无。若是只是凭一个现象,一个推测就甩锅浏览器,会不会让人有一种钦定的感受,让某些人不服呢?会的,我本身心就比较虚,不过不要紧,只要掌握了如下的关键诀窍,甩锅浏览器还不是分分钟的事。
该诀窍就是 -- 你本身去多测几个浏览器啊,朋友!!
咱们根据引擎区分不一样浏览器,
因而我下载了Firefox浏览器从新测试了原代码下页面效果,果真没有问题!
呵呵,不是我代码的问题。都是使用Webkit引擎的浏览器很差 :)。
你觉得本篇文章就这么结束了?并无(失望吧),实际上写到这里,你应该也有所感受,虽然此次debug成功解决了问题,但整个过程并不流畅高效,而且在其中走了些许弯路。结合本次debug的经验,我总结了如下几个在下次debug过程当中须要注意的方面:
debugger
关键字而不是 console.log
方法进行调试;(没错,debugger
关键字让调试更高效);以上,是我在实际开发过程当中发现问题,分析问题并解决问题的过程和思路,但愿对大家有帮助。