移動端點擊事件全攻略,這裏的坑你知多少?

看標題的時候你可能會想,點擊事件有什麼好說的,還寫一篇攻略?哈哈,若是你這麼想,只能說明你too young to simple.javascript

接觸過移動端開發的同窗可能都會面臨點擊事件的第一個問題:click事件的300ms延遲響應。不能當即響應給體驗形成了很大的困擾,所以解決這個問題就成爲了必然。css

這個問題的解決方案就是:zepto.js的tap事件。tap事件能夠理解爲在移動端的click事件,而zepto.js由於幾乎徹底複製jQuery的api,所以經常被用在h5的開發上用來取代jquery.html

因爲模塊化的緣由,致使有的同窗下載的zepto.js的模塊並不全,形成了一大堆悲劇,覺得zepto.js不支持一些方法,因此,下載的時候注意一點。java

然而事情到這裏並無結束,由於tap事件解決了一個300ms延遲問題,卻帶來了一個新的重大bug,點擊穿透react

點擊穿透的意思,就是若是一個絕對定位或者固定定位元素處於頁面最頂層,對這個元素綁定一個點擊事件,那麼你點擊這個點對應的下面凡有點擊事件或者a標籤都會被觸發執行。這裏就不貼圖了,自行腦補各類彈窗,這種狀況仍是很是多的。jquery

爲了解決這個問題,有的人試圖用touchend來搞定。touchend會在手指離開手機屏幕時觸發一次。沒有300ms延遲,沒有點擊穿透,看上去簡直就是完美的解決方案。但是!git

先在這裏簡單總結一個小知識點。github

移動端有touchstart, touchmove, touchend, 以及tap
pc端有mousedown, mousemove, mouseup, click。api

他們的關係和做用幾乎能夠對應起來,分別表示按下,滑動,鬆開。pc端能夠用前面三個事件實現拖拽,移動端也能夠用前面三個事件實現的滑動。app

因此,在pc端不用mouseup來替換click,就是由於他在鬆開鼠標的時候就會觸發,致使若是我在很遠的區域滑動到目標元素,而後鬆開,這樣的狀況也會觸發mouseup與touchend事件。因此這種狀況下是不符合點擊事件的定義的。並且若是你在某種狀況下,對某一個事件須要同時綁定拖拽和點擊,就更加沒辦法解決了。

另外還有一個很重要的緣由致使touchend不能用來替換點擊,是由於PC端不支持。老闆們經常但願本身的頁面不只僅可以在移動端展現,所以還得想其餘辦法。

我知道有經驗的同窗讀這篇文章的時候,早就在想fastclick.js了。是的,目前來看,這是一個很是好的解決方案。爲了解決300ms延遲的問題,zepto.js給出了tap事件替換的方案,而fastclick.js則是在想辦法讓click事件的延遲消除。所以任然是使用click事件,也就不會有點擊穿透的問題。

首先想辦法引入fastclick.js

<script type='application/javascript' src='/path/to/fastclick.js'></script>

若是你使用原生js開發則進行以下聲明便可。

if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
    }, false);
}

若是你想使用jquery

$(function() {
    FastClick.attach(document.body);
});

若是你在使用CommonJS風格的框架,好比requirejs

var attachFastClick = require('fastclick');
attachFastClick(document.body);

AMD

var FastClick = require('fastclick');
FastClick.attach(document.body, options);

進行對應的聲明以後,你就能夠在移動端頁面中放心大膽的使用click事件了。說到這裏,就會有一個關於zepto.js與jquery選擇的問題。提及來又能夠寫一大篇文章了,簡單來講就是,實際開發中你就會發現zepto仍是有點不太爽,雖然體積小點,既然click延遲的問題已經解決了,我仍是更偏向於使用jquery.

固然,咱們要踩的坑並無結束 - -!

最近開發了一個小頁面,財經日曆,在日曆部分,我須要同時給表明每一天的元素事件實現獲取當天資訊的需求,又要給整個日曆部分實現可以左右滑動來選取上一月和下一月的功能。因此我須要同時對日曆部分綁定click事件和實現滑動的touchstart,touchmove,touchend事件。

這個時候問題出現了。在安卓手機上,對同一個元素,若是我綁定了click事件,而後在綁定touchstart事件,click事件會處於幾乎失效的狀態。就算用了fastclick事件也沒法避免這個問題

錯誤演示大概以下

$area.on('click', '.weeknumber', function() {
    // 點擊每一天獲取當天資訊
})

// 實現左右滑動
$area.on('touchstart', function() {})
.on('touchmove', function() {})
.on('touchend', function() {})

動手能力強的同窗能夠去試試這個坑,這種狀況下,fastfclick確定是沒辦法解決的。怎麼辦?

個人第一次嘗試,是在當滑動距離爲0的時候,運行點擊事件裏面的內容。咱們知道在實現滑動[不知道如何實現的同窗,是時候關注個人公衆號了,搜索isreact找到我]的時候,會計算一個滑動距離。

// 實現滑動的大概代碼

// 滑動元素translateX的初始值
var iscroll = device_width,

    // 用來計算的中間值
    istarX = 0,
    
    // 手指第一次點在屏幕上的x座標
    istart_pageX = 0; 
   
// 綁定事件
$area.on('touchstart', touchstart)
     .on('touchmove', touchmove)
     .on('touchend', touchend);

function touchstart(event) {
    event.preventDefault();
    istartX = iscroll;
    istart_pageX = event.originalEvent.changedTouches[0].pageX;
}

function touchmove(event) {

    // 滑動過程當中手指位置x座標會不停變更,這裏會保存一個當前位置與初始位置的一個差值
    var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
    iscroll = istartX + distance;

    // 這裏是我自定義的一個css方法,用來設置元素translateX的當前值
    Utils.css(area, { translateX: iscroll });
}

function touchend(event) {
    var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
    $area.off('touchstart touchmove touchend');

    // 根據差值的不一樣,執行不一樣的動做
    if (distance < -80) {
        slideNext(function() {
            addEventSlider($area);
        })
    }
    else if (distance > 80) {
        slidePrev(function() {
            addEventSlider($area);
        })
    }
    else if (distance == 0) {        
        /* 當差值爲0時,我認爲這是執行了一次點擊 */
        
        addEventSlider($area);
    }
    else {
        ani(area).animate(400, 'easeout', { x: -device_width }, function() {
            iscroll = -device_width;
            addEventSlider($area);
        })
    }
}

上面就是個人滑動功能的實現,中間會有一些自定義的方法,所以沒辦法大家直接就複製過去運行。可是原理已經講得還算明白,能夠本身嘗試一些簡單的實現。關注我公衆號會有更加詳細的講解哦!

個人這一次嘗試,就是在滑動差值distance爲0的時候,認爲這是一次點擊,所以執行點擊事件應該有的動做。原本我覺得這就可以解決了,測試的時候也經過了。可是 - -!產品同窗很是有執念的在日曆上點擊了50多下,結果發現,屢次點擊以後,在點就失去效果了!!!

當發現這個bug的時候,個人心裏是崩潰的。好吧,只能說,試圖用touchend來代替點擊事件的想法終究仍是有一點不成熟。硬着頭皮想辦法繼續解決上面的問題。再三思考各類解決方案,最終決定本身封裝一個tap事件。封裝的tap事件的代碼以下。

http://yangbo5207.github.io/s...

這是對jquery事件的一個拓展,讓jquery也可以使用tap事件。放在jquery後面引入就可以當即使用了。除此以外還擴展了longTap,swipe兩個事件。

使用方式和其餘同樣。

$area.on('tap', function() {});

$area.tap(function() {})

固然我封裝的這個tap,任然有避免不了的點擊穿透bug,因此,最終的解決方案是:對於須要同時綁定點擊事件和滑動事件的元素,用tap事件,其餘狀況都用click事件便可。須要結合個人tap.js與fastclick.js來完美解決這個問題。心累啊,終因而搞定了。

OK,關於移動端的點擊事件總結完了,可能你都沒想到一個簡單的點擊事件會有那麼多坑,若是你在工做中可能會涉及到移動端開發的話,相信這篇文章仍是值得你點贊和收藏的,畢竟是踩了那麼多坑的經驗總結。

clipboard.png

相關文章
相關標籤/搜索