原文連接 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