Zepto tap 穿透bug、解決移動端點擊穿透問題

  當兩個層重疊在一塊兒時,或是有個彈窗,使用Zepto的tap事件時,點擊上面的一層時會觸發下面一層的事件,特別是底層若是是input框時,必「穿 透」,「google」說緣由是「tap事件其實是在冒泡到body上時才觸發」,也就是Zepto的tap事件是綁定在document上的,因此會致使,這個還未求證。
以下圖, 當點擊關閉按鈕,若是下面有商品a連接,則會穿透(關閉的同時,觸發了連接);
 
 

現象緣由:php

  zepto的tap經過兼聽綁定在document上的touch事件來完成tap事件的模擬的,及tap事件是冒泡到document上觸發的,再點擊完成時的tap事件(touchstart\touchend)須要冒泡到document上纔會觸發,而在冒泡到document以前,用 戶手的接觸屏幕(touchstart)和離開屏幕(touchend)是會觸發click事件的,由於click事件有延遲觸發(這就是爲何移動端不 用click而用tap的緣由)(大概是300ms,爲了實現safari的雙擊事件的設計),因此在執行完tap事件以後,彈出來的選擇組件馬 上就隱藏了,此時click事件還在延遲的300ms之中,當300ms到來的時候,click到的其實不是完成而是隱藏以後的下方的元素,若是正下方的 元素綁定的有click事件此時便會觸發,若是沒有綁定click事件的話就當沒click,可是正下方的是input輸入框(或者select選擇框或 者單選複選框),點擊默認聚焦而彈出輸入鍵盤,也就出現了上面的點透現象。css

 

解決方法以下:html

一、使用github上有一個叫作fastclick的庫;jquery

二、監聽touchend事件,並在事件中使用preventDefault()阻止冒泡;css3

$(".js-egg-close").on("touchend", function(e) { //這裏使用touchstart事件也是能夠的,
    e.preventDefault();
    $('.sec_dlg_eggs').remove();
    $(".eggs_bg").remove();
});

三、使用css3的pointer-events=true,pointer-events=none切換來實現;git

4.延遲必定的時間來處理事件。與jquery的動畫(fadeIn(),fadeOut())等配合感受良好;github

$(id).fadeIn(300);

5.若是還不奏效,終極解決方案是用click提代tap;app

設置點擊事件爲_tap:

_tap = touchend in document ? "touchend":"click";

這樣在執行的過程當中就能夠直接調用div.on(_tap, function(){}) 

 

下面咱們貼下zepto.1.1.6 tap事件的源碼:動畫

;(function($){  
    var touch = {},  
        touchTimeout, tapTimeout, swipeTimeout, longTapTimeout,  
        longTapDelay = 750,  
        gesture  
    
   // 判斷左右上下滑動的方向
function swipeDirection(x1, x2, y1, y2) {
     // Math.abs():返回數字的絕對值
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') } function longTap() { longTapTimeout = null if (touch.last) { touch.el.trigger('longTap') touch = {} } } function cancelLongTap() { if (longTapTimeout) clearTimeout(longTapTimeout) longTapTimeout = null } function cancelAll() { if (touchTimeout) clearTimeout(touchTimeout) if (tapTimeout) clearTimeout(tapTimeout) if (swipeTimeout) clearTimeout(swipeTimeout) if (longTapTimeout) clearTimeout(longTapTimeout) touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null touch = {} } function isPrimaryTouch(event){ return (event.pointerType == 'touch' || event.pointerType == event.MSPOINTER_TYPE_TOUCH) && event.isPrimary } function isPointerEventType(e, type){ return (e.type == 'pointer'+type || e.type.toLowerCase() == 'mspointer'+type) } $(document).ready(function(){ var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType if ('MSGesture' in window) { gesture = new MSGesture() gesture.target = document.body } $(document) .bind('MSGestureEnd', function(e){ var swipeDirectionFromVelocity = e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null; if (swipeDirectionFromVelocity) { touch.el.trigger('swipe') touch.el.trigger('swipe'+ swipeDirectionFromVelocity) } }) .on('touchstart MSPointerDown pointerdown', function(e){ if((_isPointerType = isPointerEventType(e, 'down')) && !isPrimaryTouch(e)) return firstTouch = _isPointerType ? e : e.touches[0] if (e.touches && e.touches.length === 1 && touch.x2) { // Clear out touch movement data if we have it sticking around // This can occur if touchcancel doesn't fire due to preventDefault, etc. touch.x2 = undefined touch.y2 = undefined } now = Date.now() delta = now - (touch.last || now) touch.el = $('tagName' in firstTouch.target ? firstTouch.target : firstTouch.target.parentNode) touchTimeout && clearTimeout(touchTimeout) touch.x1 = firstTouch.pageX touch.y1 = firstTouch.pageY if (delta > 0 && delta <= 250) touch.isDoubleTap = true touch.last = now longTapTimeout = setTimeout(longTap, longTapDelay) // adds the current touch contact for IE gesture recognition if (gesture && _isPointerType) gesture.addPointer(e.pointerId); }) .on('touchmove MSPointerMove pointermove', function(e){ if((_isPointerType = isPointerEventType(e, 'move')) && !isPrimaryTouch(e)) return firstTouch = _isPointerType ? e : e.touches[0] cancelLongTap() touch.x2 = firstTouch.pageX touch.y2 = firstTouch.pageY deltaX += Math.abs(touch.x1 - touch.x2) deltaY += Math.abs(touch.y1 - touch.y2) }) .on('touchend MSPointerUp pointerup', function(e){ if((_isPointerType = isPointerEventType(e, 'up')) && !isPrimaryTouch(e)) return cancelLongTap() // swipe if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) swipeTimeout = setTimeout(function() { touch.el.trigger('swipe') touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) touch = {} }, 0) // normal tap else if ('last' in touch) // don't fire tap when delta position changed by more than 30 pixels, // for instance when moving to a point and back to origin if (deltaX < 30 && deltaY < 30) { // delay by one tick so we can cancel the 'tap' event if 'scroll' fires // ('tap' fires before 'scroll') tapTimeout = setTimeout(function() { // trigger universal 'tap' with the option to cancelTouch() // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) var event = $.Event('tap') event.cancelTouch = cancelAll touch.el.trigger(event) // trigger double tap immediately if (touch.isDoubleTap) { if (touch.el) touch.el.trigger('doubleTap') touch = {} } // trigger single tap after 250ms of inactivity else { touchTimeout = setTimeout(function(){ touchTimeout = null if (touch.el) touch.el.trigger('singleTap') touch = {} }, 250) } }, 0) } else { touch = {} } deltaX = deltaY = 0 }) // when the browser window loses focus, // for example when a modal dialog is shown, // cancel all ongoing events .on('touchcancel MSPointerCancel pointercancel', cancelAll) // scrolling the window indicates intention of the user // to scroll, not tap or swipe, so cancel all ongoing events $(window).on('scroll', cancelAll) }) ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){ $.fn[eventName] = function(callback){ return this.on(eventName, callback); } }) })(Zepto)

 

 
相關文章
相關標籤/搜索