原文连接 http://ymblog.net/2016/03/28/由移动端页面点击穿透想到的/javascript
首先我想到了哪些:css
多是由click事件的延迟或者事件冒泡致使,事件包含touchstart/touchmove/touchend/mousedown/mousemove/mouseup/click。html
形成点击穿透的缘由是,在移动端页面中click的点击事件有200-300ms的延迟,为了体验更好咱们使用了zepto的touch事件,或者说使用的touchstart/touchend事件,在执行完 touch事件以后,目标事件执行,此时 click 事件还在延迟的 300ms 之中。当 300ms 到来的时候,click 到的实际上是隐藏元素下方的元素。java
若是正下方的元素有绑定 click 事件,此时便会触发,若是没有绑定 click 事件的话就当没发生。若是正下方的是 input 输入框(或是 select / radio / checkbox),点击默认 focus 而弹出输入键盘,也就出现了上面的“点透”现象。android
那为何要有延迟?浏览器在 touchend 后会等待约300ms,缘由是判断用户是否有双击(double tap)行为。若是没有 tap 行为,则触发 click 事件,而双击过程当中就不适合触发 click 事件了。由此能够看出 click 事件触发表明一轮触摸事件的结束。ios
DOM事件流:web
1,事件捕获阶段;segmentfault
2,处于目标阶段;浏览器
3,事件冒泡阶段。dom
说到这儿咱们再来了解下事件的执行顺序,在移动端页面中,touch事件的执行顺序是touchstart
->touchmove
-> touchend -> mousedown ->mouseup ->click,固然在web端中可能顺序又不同。
那彷佛上面说的穿透问题变得理所固然了。由于这是浏览器的默认行为!
通常来讲加上下面的代码就会解决问题了。
在touchend事件中阻止浏览器的默认行为,就能够了。
event.preventDefault();
上面的算是一种解决办法了,我记得还会有那种状况即便加了这行语句仍是无效的更特殊的状况,有人说使用fastclick能够解决问题,当初我使用的时候发现确实好像点击快了不少,可是在华为荣耀3C的机型上却发现仍是会出现点击穿透的现象,再考虑到为此我须要再引入一个文件的http的请求,因此果断放弃。
还有一种治标不治本的方式,以下:
因为 click 事件的滞后性,在这段时间内原来点击的元素消失了,因而便“穿透”了。所以咱们顺着这个思路就想到,能够给元素的消失作一个fade效果,相似jQuery里的fadeOut
,并设置动画duration大于300ms,这样当延迟的 click 触发时,就不会“穿透”到下方的元素了。
一样的道理,不用延时动画,咱们还能够动态地在触摸位置生成一个透明的元素,这样当上层元素消失而延迟的click来到时,它点击到的是那个透明的元素,也不会“穿透”到底下。在必定的timeout后再将生成的透明元素移除。
$('#closePopup').on('tap', function(e){ $('#popupLayer').hide(); $('#bgMask').hide(); $('#underLayer').css('pointer-events', 'none'); setTimeout(function(){ $('#underLayer').css('pointer-events', 'auto'); }, 400); });
但若是页面中的点击事件多了这样确定不行的。
zepto上的tap事件也是模拟出来的,咱们彷佛能够本身来模拟点击事件。
咱们能够打印控制台打印的event对象,
所谓事件模拟就是经过特定的方法,传入指定的参数,而后派发这个自定义事件就能够了。
var event = document.createEvent('MouseEvents');
这个参数可选的有:
var type = 'click'; //要触发的事件类型 var bubbles = true; //事件是否能够冒泡 var cancelable = true; //事件是否能够阻止浏览器默认事件 var view = document.defaultView; //与事件关联的视图,该属性默认便可,无论 var detail = 0; var screenX = 0; var screenY = 0; var clientX = 0; var clientY = 0; var ctrlKey = false; //是否按下ctrl var altKey = false; //是否按下alt var shiftKey = false; var metaKey = false; var button = 0;//表示按下哪个鼠标键 var relatedTarget = 0; //模拟mousemove或者out时候用到,与事件相关的对象 var event = document.createEvent('MouseEvents'); event.initMouseEvent(type, bubbles, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
最终的解决办法:
var el = null; var list = document.getElementById('list'); function getEvent(el, e, type) { e = e.changedTouches[0]; var event = document.createEvent('MouseEvents'); event.initMouseEvent(type, true, true, window, 1, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null); //event.forwardedTouchEvent = true; return event; } list.addEventListener('touchstart', function (e) { var firstTouch = e.touches[0] el = firstTouch.target; }) list.addEventListener('touchend', function (e) { e.preventDefault(); var event = getEvent(el, e, 'click'); el.dispatchEvent(event); }) list.addEventListener('click', function (e) { list.style.display = 'none'; setTimeout(function () { list.style.display = ''; }, 1000); })
咱们能够在touchend事件的时候阻止浏览器的默认行为,而后本身模拟事件的产生与触发。这就是本身建立一个click的Event对象触发之,全部的点击依然就用click。
从早上看资料看到如今,刚开始思路还很是清晰,问题的来源,问题的产生等等以及该如何解决...看的时间久了,看的东西多了忽然发现变得模糊了起来,发现不少东西都是冲突的,好比点击穿透是zepto引发的?看了下网友说的,因为zepto的模拟tap事件是冒泡到document才触发的,而在冒泡到 document 以前,手指接触和离开屏幕(touchstart / touchend)是会触发 click 事件的。因此就会有这个问题?那我阻止了浏览器的默认事件不就ok了?
但彷佛又是说在安卓和iOS的区别在于mousedown的执行速度,安卓大于iOS
在android上得到的结果是惊人的,这个劳什子android里面moveover事件偶然比尼玛touchstart还快!!!
而ios压根就不理睬mouseover事件,这是主要问题产生缘由!!!
而android在movedown时候,开开心心触发了input的focus事件,而后键盘就弹起来了!!!
因此针对android,咱们还得将mousedown干掉才行!!!!
而事实上,咱们input获取焦点,就是经过mousedown触发的,ios也是,因此要解决android下面的问题还得从其它层面抓起
这也就产生了最终的解决办法:也就是上面写的,上面的只是针对一个点击事件,下面则针对所有:
var touch = {}; var t = new Date().getTime(); document.addEventListener('click', function (event) { if (event.myclick == true) { return true; } if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { event.propagationStopped = true; } event.stopPropagation(); event.preventDefault(); return true; }, true); document.addEventListener('touchstart', function (e) { touch.startTime = e.timeStamp; touch.el = e.target; t = e.timeStamp; }); document.addEventListener('touchmove', function (e) { }); document.addEventListener('touchend', function (e) { touch.last = e.timeStamp; var event = document.createEvent('Events'); event.initEvent('click', true, true, window, 1, e.changedTouches[0].screenX, e.changedTouches[0].screenY, e.changedTouches[0].clientX, e.changedTouches[0].clientY, false, false, false, false, 0, null); event.myclick = true; touch.el && touch.el.dispatchEvent(event); return true; }); dom.addEventListener('click', function (e) { alert(1); });
其核心是本身模拟事件并派发。
var event = document.createEvent('Events'); event.initEvent('click', true, true, window, 1, e.changedTouches[0].screenX, e.changedTouches[0].screenY, e.changedTouches[0].clientX, e.changedTouches[0].clientY, false, false, false, false, 0, null); event.myclick = true; touch.el && touch.el.dispatchEvent(event);
最早点开的这篇文章:https://segmentfault.com/q/1010000000691822
后来又点开了这篇文章:http://www.javashuo.com/article/p-amypmlne-db.html
而后在这篇文章的页脚又看到了不少,因而乎又查看了这篇:http://www.cnblogs.com/yexiaochai/p/3462657.html