由移動端頁面點擊穿透想到的

原文連接 http://ymblog.net/2016/03/28/由移動端頁面點擊穿透想到的/javascript

首先我想到了哪些:css

  1. 點擊穿透是如何引發的
  2. 如何解決
  3. 什麼是事件模擬

1、點擊穿透是如何引發的?

多是由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端中可能順序又不同。

那彷佛上面說的穿透問題變得理所固然了。由於這是瀏覽器的默認行爲! 

 

2、如何解決

通常來講加上下面的代碼就會解決問題了。

在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);
});

  

但若是頁面中的點擊事件多了這樣確定不行的。

 

3、什麼是事件模擬

zepto上的tap事件也是模擬出來的,咱們彷佛能夠本身來模擬點擊事件。

咱們能夠打印控制檯打印的event對象,

`]FUU[}715{5KU7@LASSWCK

所謂事件模擬就是經過特定的方法,傳入指定的參數,而後派發這個自定義事件就能夠了。

var event = document.createEvent('MouseEvents');

  

這個參數可選的有:

  1. UIEvents
  2. MouseEvents
  3. MutationEvents,通常化dom變更
  4. HTMLEvents通常dom事件
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。

 

4、總結

從早上看資料看到如今,剛開始思路還很是清晰,問題的來源,問題的產生等等以及該如何解決...看的時間久了,看的東西多了忽然發現變得模糊了起來,發現不少東西都是衝突的,好比點擊穿透是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);

5、參考

最早點開的這篇文章:https://segmentfault.com/q/1010000000691822

後來又點開了這篇文章:http://www.javashuo.com/article/p-amypmlne-db.html

而後在這篇文章的頁腳又看到了不少,因而乎又查看了這篇:http://www.cnblogs.com/yexiaochai/p/3462657.html

而後又看了這篇:http://www.cnblogs.com/yexiaochai/p/3442220.html

相關文章
相關標籤/搜索