Vue.js 移动端 Web App 点击穿透问题解决方案

<h2 id="articleHeader0">描述</h2> <p>在近期的一个移动端项目中,有一个页面需要有弹框提示,并且这个弹框通过关闭按钮关闭。页面当中使用了 iScroll 来实现页面局部滚动,在 iScroll 的配置当中把 <code>tap</code> 和 <code>click</code> 事件都开启了。<br />代码如下:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="this.myScroll = new IScroll(this.$refs.wrapper, { mouseWheel: true, click: true, tap: true })" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs kotlin"><code><span class="hljs-keyword">this</span>.myScroll = new IScroll(<span class="hljs-keyword">this</span>.$refs.wrapper, { mouseWheel: <span class="hljs-literal">true</span>, click: <span class="hljs-literal">true</span>, tap: <span class="hljs-literal">true</span> })</code></pre> <p>在实现过程中,遇到了一个奇怪的问题,由于按钮的位置与弹框右上角的关闭按钮位置一致,当我点击按钮时,弹框一闪而过。</p> <p>效果如下:<br /><span class="img-wrap"><img data-src="/img/remote/1460000010488535" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="效果图" title="效果图" style="cursor: pointer;"></span></p> <h2 id="articleHeader1">原因</h2> <h3 id="articleHeader2">什么是点击穿透?</h3> <p>假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。</p> <p>通过上网查找有关资料,翻阅了移动端的书籍,发现在手机端中,事件的触发顺序为:<code>touchstart</code> -&gt; <code>touchmove</code> -&gt; <code>touchend</code>,而 <code>click</code> 事件有 300ms 的延迟,当 <code>touchstart</code> 事件把B元素隐藏之后,隔了300ms,浏览器触发了 <code>click</code> 事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。</p> <h2 id="articleHeader3">解决方案</h2> <h3 id="articleHeader4">1. 改用 <code>touch</code> 事件</h3> <p>由于项目使用的是 <code>Vue.js</code>,这里就提供一下 <code>Vue.js</code> 的解决方法。使用了 <a href="https://github.com/MeCKodo/vue-tap" rel="nofollow noreferrer" target="_blank">vue-tap</a> 的一个插件,具体使用方法参看官方文档,在需要点击事件的时候,通过 <code>v-tap</code> 指令来绑定。</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// main.js import vueTap from 'v-tap' // 引入插件 Vue.use(vueTap) // 全局注册" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-comment">// main.js</span> <span class="hljs-keyword">import</span> vueTap <span class="hljs-keyword">from</span> <span class="hljs-string">'v-tap'</span> <span class="hljs-comment">// 引入插件</span> Vue.use(vueTap) <span class="hljs-comment">// 全局注册</span></code></pre> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="v-tap=&quot;{methods:showReceiveModel}&quot; // 在元素上绑定事件" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript" style="word-break: break-word; white-space: initial;">v-tap=<span class="hljs-string">"{methods:showReceiveModel}"</span> <span class="hljs-comment">// 在元素上绑定事件</span></code></pre> <h3 id="articleHeader5">2. 使用 fastclick 插件</h3> <p>这个也是在网上看到的,也可以解决点透问题,使用方法可以看 <a href="https://github.com/ftlabs/fastclick" rel="nofollow noreferrer" target="_blank">fastclick</a> 的文档,在这里提供一下 <code>Vue.js</code> 的引入及使用</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="import FastClick from 'fastclick'; // 引入插件 FastClick.attach(document.body, options); // 使用 fastclick" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs typescript"><code><span class="hljs-keyword">import</span> FastClick <span class="hljs-keyword">from</span> <span class="hljs-string">'fastclick'</span>; <span class="hljs-comment">// 引入插件</span> FastClick.attach(<span class="hljs-built_in">document</span>.body, options); <span class="hljs-comment">// 使用 fastclick</span></code></pre> <p>最终没有使用这个方案是因为有一些小 bug ,如 <a href="http://blog.csdn.net/forevercjl/article/details/46730157" rel="nofollow noreferrer" target="_blank">Fastclick 导致click事件触发两次的问题</a>。</p> <h2 id="articleHeader6">其他</h2> <h3 id="articleHeader7"> <code>tap</code> 一词</h3> <p>对于 <code>tap</code> 这个词,用过 <code>Zepto</code> 或 <code>KISSY</code> 等移动端js库的人肯定对tap事件不陌生,做PC页面时绑定 <code>click</code>,相应地手机页面就绑定 <code>tap</code>。但原生的 <code>touch</code> 事件本身是没有 <code>tap</code> 的,js库里提供的tap事件都是模拟出来的。</p> <p>手机上响应 <code>click</code> 事件会有300ms的延迟,那么这300ms到底是干嘛了?浏览器在 <code>touchend</code> 后会等待约300ms,原因是判断用户是否有双击<code>(double tap)</code>行为。如果没有 <code>tap</code> 行为,则触发 <code>click</code> 事件,而双击过程中就不适合触发 <code>click</code> 事件了。由此可以看出 <code>click</code> 事件触发代表一轮触摸事件的结束。</p> <div class="google-auto-placed ap_container" style="text-align: center; width: 100%; height: auto; clear: none;"><ins data-ad-format="auto" class="adsbygoogle adsbygoogle-noablate" data-ad-client="ca-pub-6330872677300335" data-adsbygoogle-status="done" style="display: block; margin: auto; background-color: transparent;"><ins id="aswift_4_expand" style="display: inline-table; border: none; height: 0px; margin: 0px; padding: 0px; position: relative; visibility: visible; width: 697px; background-color: transparent;"><ins id="aswift_4_anchor" style="display: block; border: none; height: 0px; margin: 0px; padding: 0px; position: relative; visibility: visible; width: 697px; background-color: transparent; overflow: hidden; opacity: 0;"><iframe id="aswift_4" name="aswift_4" width="697" height="175" frameborder="0" src="https://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-6330872677300335&amp;output=html&amp;h=175&amp;adk=1934095149&amp;adf=849612825&amp;w=697&amp;lmt=1557230568&amp;num_ads=1&amp;sem=mc&amp;pwprc=5909933996&amp;guci=2.2.0.0.2.2.0.0&amp;ad_type=text&amp;format=697x175&amp;url=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000010488530&amp;flash=0&amp;pra=3&amp;wgl=1&amp;fa=27&amp;adsid=NT&amp;dt=1557230568324&amp;bpp=1&amp;bdt=1120&amp;idt=-M&amp;shv=r20190429&amp;cbv=r20190131&amp;saldr=aa&amp;abxe=1&amp;prev_fmts=0x0%2C970x90%2C728x90&amp;nras=2&amp;correlator=6755537900076&amp;frm=20&amp;pv=1&amp;ga_vid=1995558.1557230568&amp;ga_sid=1557230568&amp;ga_hid=659436775&amp;ga_fc=0&amp;iag=0&amp;icsg=19866462336&amp;dssz=38&amp;mdo=0&amp;mso=8&amp;u_tz=480&amp;u_his=1&amp;u_java=0&amp;u_h=1080&amp;u_w=1920&amp;u_ah=1040&amp;u_aw=1920&amp;u_cd=24&amp;u_nplug=1&amp;u_nmime=1&amp;adx=31&amp;ady=2358&amp;biw=1001&amp;bih=717&amp;scr_x=0&amp;scr_y=0&amp;eid=20040013%2C21060853%2C21063154&amp;oid=3&amp;ref=https%3A%2F%2Fsegmentfault.com%2Fsearch%3Fq%3Dvue%26type%3Darticle%26page%3D133&amp;rx=0&amp;eae=0&amp;fc=1424&amp;brdim=447%2C192%2C447%2C192%2C1920%2C0%2C1018%2C717%2C1018%2C717&amp;vis=1&amp;rsz=%7C%7Cs%7C&amp;abl=NS&amp;fu=8216&amp;bc=13&amp;osw_key=3581259108&amp;ifi=4&amp;uci=4.qxzglvd696qw&amp;xpc=ZBn2o6LxfU&amp;p=https%3A//segmentfault.com&amp;dtd=4" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" data-google-container-id="4.qxzglvd696qw" data-load-complete="true" data-google-query-id="CIWR6suwieICFVmIwgod3jAO2A"></iframe></ins></ins></ins></div> <p>既然说tap事件是模拟出来的,我们可以看下 <code>Zepto</code> 对 singleTap 事件的处理。见 <a href="https://github.com/madrobby/zepto/blob/master/src/touch.js#L136-L143" rel="nofollow noreferrer" target="_blank">源码 136-143 行</a>,可以看出在 <code>touchend</code>响应 250ms 无操作后,则触发 <code>singleTap</code>。</p> <h2 id="articleHeader8">参考</h2> <h3 id="articleHeader9">博文</h3> <ul> <li><a href="https://segmentfault.com/a/1190000003848737">也来说说touch事件与点击穿透问题</a></li> <li><a href="http://www.ayqy.net/blog/%E7%A7%BB%E5%8A%A8%E9%A1%B5%E9%9D%A2%E7%82%B9%E5%87%BB%E7%A9%BF%E9%80%8F%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" rel="nofollow noreferrer" target="_blank">移动页面点击穿透问题解决方案</a></li> <li><a href="http://www.cnblogs.com/shytong/p/5463673.html" rel="nofollow noreferrer" target="_blank">点击穿透原理及解决</a></li> </ul> <h3 id="articleHeader10">书籍</h3> <ul> <li>《移动 Web 手册》</li> </ul> <blockquote><p>以上部分资料搜集整理自网络,如有不对的地方希望及时告知,欢迎大家批评指正,谢谢!</p></blockquote>
脚本宝典为你提供优质服务
脚本宝典 » Vue.js 移动端 Web App 点击穿透问题解决方案

发表评论

提供最优质的资源集合

立即查看 了解详情