关注我的你应该知道,我最近适配了PC端的移动端页面。在这个过程中,为了给用户节省300ms的时间,给用户更及时的点击反馈(也就是更好的用户体验),我尝试用移动端特有的touchstart事件来代替传统的点击事件。这个过程我遇到了一些小问题,也顺利解决了。具体情况可以通过本文查看。所谓祸不单行。发布即将上线时,突然发现在使用touchstart事件后,移动端又出现了一个奇怪的现象:当用户滚动页面时,原本绑定在固定定位的navbar元素上的touchstart事件会定时失效时间。您可以通过扫描下方二维码并使用您的Safari浏览器或Chrome浏览器(注意:不是浏览器自带的模拟器)来亲身体验这种奇怪的现象。当然,最后我设法解决了这个问题,有趣的是,这个问题似乎不是我的代码引起的,而是我将其归咎于浏览器的错误。但是对于这个bug的原理我也只是粗略的猜测。如果您知道造成这种现象的原因,欢迎您与我分享。在这篇文章中,我不仅会记录我的解决方案,还会记录我遇到这个问题后的调试过程和分析思路。但如果你正在为这个问题苦苦挣扎,只想快速摆脱它,你可以向下滚动到文章底部的“解决方案”部分,并参考我的解决方案(我很可爱,对吧??).一、问题描述移动设备:iPhone6操作系统:iOS11.2.5测试浏览器:Safari、Chrome点此查看示例代码。,绑定到navbar元素的touchstart事件不会被触发(或者偶尔不会触发)。2.分析与排查(1)第一步:使用搜索引擎无意中发现了这个问题。我当时观察到的是,navbar绑定的touchstart事件有时会触发,有时会失败。于是用谷歌搜索关键字“touchstarteventoccasionallyfails”,遗憾的是,没有可靠的答案。这说明“不存在touchstart偶尔失败的问题”。也就是说,我需要找出到底是什么破坏了touchstart响应事件。接下来继续在移动设备上尝试各种操作(双击、滚动屏幕、放大等),并注意移动设备的响应,最终确定是touchstart绑定事件的原因失败是“页面滚动后”。接下来的事情就很简单了,继续谷歌搜索“touchstarteventinvalidafterpagescroll”。观察第一个屏幕上的搜索结果,然后单击以查看它们。可惜没有合适的资料:于是抱着试一试的态度转百度搜索相同的关键词。搜索结果,但是看了之后发现还是不是我想要的:终极杀手的时候到了,用英文关键词搜索!这是我的谷歌搜索关键字“页面滚动后触摸事件不响应”的结果:老样子,仍然没有令人满意的结果。至此我得到了两条信息:可能是我的代码有误:因为有很小的可能是以前从未遇到过的奇怪现象;我应该从触摸事件的相关概念中寻找原因;(2)第2步:隔离代码,理清概念至此,第一阶段调试结束,我的徒劳说明这个问题不简单,需要认真对待,然后我采用了以下两种方法:提取核心代码,通过隔离外界判断因素,排除外因;查阅MDN上触摸事件的文档,找出可能导致此类问题的内部原因;思路很清晰对吧,但是我没有在相关文档中找到任何可能导致这个问题的灵感。好在我已经通过第一步把问题说的很清楚了,所以不要气馁,继续思考。(3)第三步:大胆假设,仔细验证基本上在这个阶段,调试过程已经进入了经验和直觉领域。要成功解决这个问题,有时需要一点运气。我在这个过程中尝试了以下解决方案:“不假思索地尝试就对了”:我会把在搜索引擎中看到的问题的一些解决方案一一尝试,希望一个能奏效,也能得到更多的信息来定位问题的原因。这些尝试包括:在事件回调函数中加入e.preventDefault()方法,将touchstart事件替换为touchend事件或直接点击事件。尴尬了,这些尝试都没有奏效,问题依然存在,不过无所谓了。我对这次短暂的尝试并没有抱太大期望,但它实际上表明这种奇怪的现象与触摸的特定事件类型无关,与触摸事件意外触发其他事件无关。到目前为止,我知道我使用触摸事件的方式是正确的,没有其他因素可以干扰点击事件的触发。自然地,我开始怀疑浏览器是否真的检测到我手指的“点击”。这可以通过下面的代码来回答:window.addEventListener("touchstart",e=>{console.log(e.target)})奇妙的事情发生了,我的navbar不再出现toucheventfailsafterpagescrolling问题!但是当我按照相同的想法将代码替换为以下代码以查看返回值时:varnavbar=document.querySelector(".navbar")navbar.addEventListener("touchstart",e=>{console.log(e.target)})问题又出现了,当页面滚动的时候,每当我再次点击导航栏时,控制台没有任何输出,也就是说浏览器认为我没有点击导航栏!这不科学,但我已经看到了胜利的曙光。当我将原本绑定在navbar上的touchstart事件通过事件冒泡机制绑定到window对象上,通过判断e.target属性进行事件回调时——问题解决,页面正常,整个世界都是clean...最终解决代码如下:varnavbar=document.querySelector(".navbar")window.addEventListener("touchstart",e=>{if(e.target===navbar){//callback}})扫描二维码查看正确效果:目前问题是否顺利解决?一点也不。世界被净化的那一刻虽然让人耳目一新,但只是需求得到了满足,问题还没有解决。我的意思是我心中的疑问:“为什么这个有效,而那个却无效?”.这个问题很重要,希望大家不要忽视。你同意?让我们继续吧。让我们回过头来再次分析我们的代码。很明显,它非常精简。唯一可能的问题是我们设置了导航栏的固定位置。让我们想想我们是如何通过“误击”来解决这个问题的,导航栏无法检测到我们的点击,但窗口可以。把这两条线索放在一起想一想,我想到了一个非常可疑的对象:Hierarchy。我尝试删除位置:已修复;导航栏的声明,果然,一切都恢复正常了。看来这种说法是造成这种奇怪现象的原因。而我能想到的因素就是层级,我指的是DOM对象的层级。最后我是这样解释这个奇怪现象的原因的:在页面初始化的时候,浏览器的DOM树是正确渲染的,也就是说DOM元素之间的关系是正确的,所以navbar元素可以准确捕捉我们的touchstart事件,但是当页面滚动时,浏览器失去了navbar元素的层级关系,touchstart事件无法通过冒泡的方式被navbar元素捕捉到,所以我们绑定的事件没有响应。而当我们让整个window对象监听touchstart事件时,浏览器可以正确的重新计算DOM对象之间的关系,navbar层也可以捕捉到冒泡事件,所以一切正常。这个解释让你信服吗?其实我也不知道,毕竟这只是我根据现象的推测。但不管怎么说,这种奇怪的现象应该归咎于浏览器,而不是我的代码(哈,松了一口气)。至此,问题结束了吗?一点也不。如果仅仅根据一种现象或一种推测来责备浏览器,会不会让人产生肯定而使一些人不满意呢?是的,我心脏不好,不过没关系,只要掌握以下关键技巧,怪罪浏览器不是分分钟的事。诀窍是——自己多测试几个浏览器,朋友!!我们根据引擎区分不同的浏览器。使用Webkit引擎的浏览器:Chrome、Safari使用Gecko引擎的浏览器:Firefox使用Presto引擎的浏览器:Opera于是下载了Firefox浏览器,在原代码下重新测试了页面效果。果然没有问题!哦,这不是我的代码问题。所有使用Webkit引擎的浏览器都不好:)。3.小结你觉得这篇文章到此结束了吗?没有(失望),其实写到这里,大家应该也觉得虽然这次debug成功解决了问题,但是整个过程并不顺利高效,其中还有些弯路。结合这次调试的经验,我总结了下一次调试过程中需要注意的几个方面:使用Google、Google(英文关键词)、百度搜索引擎依次搜索关键词;保持警惕(检查自己的代码);编写demo,隔离核心代码,简化分析后台;确保你有相关的技术知识点;先用各个浏览器进行测试(这样可以尽快排除是不是浏览器的bug);尽早使用debugger关键字代替console.log方法进行调试;(是的,debugger关键字使调试更有效率);仔细观察问题,大胆假设,认真求证;不要放弃,解决了需求,就要解决问题;如果有时间和精力,把调试过程中发现的问题和学到的知识总结在博客中:);以上就是实际开发过程中发现问题、分析问题、解决问题的过程和思路,希望对大家有所帮助。
