移動web開發之touch事件

前面的話

  iOS版Safari爲了向開發人員傳達一些特殊信息,新增了一些專有事件。由於iOS設備既沒有鼠標也沒有鍵盤,因此在爲移動Safari開發交互性網頁時,常規的鼠標和鍵盤事件根本不夠用。隨着Android 中的WebKit的加入,不少這樣的專有事件變成了事實標準,致使W3C開始制定Touch Events規範。本文將詳細介紹移動端touch事件html

 

概述

  包含iOS 2.0軟件的iPhone 3G發佈時,也包含了一個新版本的Safari瀏覽器。這款新的移動Safari提供了一些與觸摸(touch)操做相關的新事件。後來,Android上的瀏覽器也實現了相同的事件。觸摸事件會在用戶手指放在屏幕上面時、在屏幕上滑動時或從屏幕上移開時觸發。具體來講,有如下幾個觸摸事件chrome

touchstart:當手指觸摸屏幕時觸發;即便已經有一個手指放在了屏幕上也會觸發
touchmove:當手指在屏幕上滑動時連續地觸發。在這個事件發生期間,調用preventDefault()能夠阻止滾動
touchend:當手指從屏幕上移開時觸發
touchcancel:當系統中止跟蹤觸摸時觸發(不經常使用)。關於此事件的確切觸發時間,文檔中沒有明確說明

【touchenter 和 touchleave】數組

  觸摸事件規範中曾經包含touchenter和touchleave事件,這兩個事件在用戶手指移入或移出某個元素時觸發。可是這兩個事件歷來沒有被實現。微軟有這兩個事件的替代事件,可是隻有IE瀏覽器支持。某些狀況下能夠知道用戶手指滑入滑出某個元素是素是很是有用的,因此但願這兩個事件能夠重返規範瀏覽器

  在觸摸事件中,經常使用的是touchstart、touchumove和touchend這三個事件,與鼠標事件的對應以下dom

鼠標         觸摸          
mousedown   touchstart 
mousemove   touchmove   
mouseup     touchend     

   [注意]touch事件在chrome模擬器下部分版本使用DOM0級事件處理程序的方式來添加事件無效ide

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    #test{height:200px;width:200px;background:lightblue;}
  </style>
</head>
<body>
<div id="test"></div>
<script>
  (function(){ 
    var 
      stateMap = {
        touchstart_index : 0,
        touchmove_index  : 0,
        touchend_index   : 0
      },
      elesMap = {
        touch_obj: document.getElementById('test')
      },
      showIndex, handleTouch;

    showIndex = function ( type ) {
      elesMap.touch_obj.innerHTML = type + ':' + (++stateMap[type + '_index']);
    };

    handleTouch = function ( event ) {
      showIndex( event.type );
    };

    elesMap.touch_obj.addEventListener('touchstart', function(event){handleTouch(event);}); 
    elesMap.touch_obj.addEventListener('touchmove', function(event){handleTouch(event);});
    elesMap.touch_obj.addEventListener('touchend', function(event){handleTouch(event);});
  })(); 

</script>
</body>
</html>

 

300ms

  300ms問題是指在某個元素執行它的功能和執行touch事件之間有一個300毫秒的間隔。鼠標事件、焦點事件、瀏覽器默認行爲等相較於touch事件,都存在着300ms的延遲函數

【點透】佈局

  由於300ms的存在,會形成常見的點透問題。先來看例子動畫

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    #test {position: absolute;top: 0;left: 0;opacity: 0.5;height: 200px;width: 200px;background: lightblue;}
  </style>
</head>
<body>
  <a href="https://baidu.com">百度</a>
  <div id="test"></div>
  <script>
    (function () {
      var
        elesMap = {
          touchObj: document.getElementById('test')
        },
        fnHide, onTouch;
      fnHide = function (type) {
        elesMap.touchObj.style.display = 'none';
      };
      onTouch = function (event) {
        fnHide();
      };
      elesMap.touchObj.addEventListener('touchstart', function(event){onTouch(event);});
    })(); 
  </script>
</body>
</html>

  淺藍色的半透明div被點擊(觸發touch事件)後,若是點擊位置正好位於連接的上方,則會觸發連接跳轉的默認行爲。詳細解釋是,點擊頁面後,瀏覽器會記錄所點擊的頁面座標,300ms後,在該座標找到元素。在該元素上觸發點擊行爲。所以,若是300ms內同一頁面座標的上層元素消失後,300ms後在下層元素上觸發點擊行爲。這就形成了點透問題ui

  形成這個問題,是由於觸摸屏幕的行爲被重載(overload)了。在手指觸摸屏幕的瞬間,瀏覽器沒法預知用戶是在輕觸(Tap)、雙觸(Double-Tap)、滑動(Swipe)、按住不放(Hold)仍是其餘什麼操做。惟一保險的作法就是等上一下子看接下來會發生什麼

  問題是在於雙觸(Double-Tap)。即使是瀏覽器檢測出手指離開了屏幕,它仍然沒法判斷接下來作什麼。由於瀏覽器沒法知道手指是會再次回到屏幕,仍是就此結束觸發輕觸事件以及事件級聯。爲了肯定這一點,瀏覽器不得不等待一小段時間。瀏覽器開發者找到一個最佳時間間隔,就是300毫秒

【解決辦法】

  一、在touch事件的事件處理程序中增長300ms的延遲

    (function () {
      var
        elesMap = {
          touchObj: document.getElementById('test')
        },
        fnHide, onTouch;
      fnHide = function (type) {
        elesMap.touchObj.style.display = 'none';
      };
      onTouch = function (event) {
        setTimeout(function(){
          fnHide();
        },300);
      };
      elesMap.touchObj.addEventListener('touchstart', function (event) { onTouch(event); });
    })(); 

  二、使用緩動動畫,增長300ms的過渡效果,注意display屬性沒法使用transition

  三、加入中間層的dom元素,讓中間層接受這個穿透事件,稍後隱藏

  四、上下兩級都使用tap事件,但默認行爲不可避免

  五、在document上的touchstart事件,阻止默認行爲。

document.addEventListener('touchstart',function(e){
    e.preventDefault();
})

  接着,添加a標籤的跳轉行爲

a.addEventListener('touchstart',function(){
  window.location.href = 'https://cnblogs.com';  
})

  可是,這種方法有反作用,會形成頁面沒法滾動、文本沒法選中等。若是在某個元素上,須要恢復文本選中的行爲,則可使用阻止冒泡來恢復

el.addEventListener('touchstart',function(e){
    e.stopPropagation();
})

  

事件對象

【基礎信息】

  每一個觸摸事件的event對象都提供了在鼠標事件中常見的屬性,包括事件類型、事件目標對象、事件冒泡、事件流、默認行爲等

   以touchstart爲例,示例代碼以下

  <script>
    (function () {
      var
        elesMap = {
          touchObj: document.getElementById('test')
        },
        onTouch;
      onTouch = function (e) {
          console.log(e)
    };
      elesMap.touchObj.addEventListener('touchstart', function (event) { onTouch(event); });
    })(); 
  </script>

  一、currentTarget屬性返回事件正在執行的監聽函數所綁定的節點

  二、target屬性返回事件的實際目標節點

  三、srcElement屬性與target屬性功能一致

//當前目標
currentTarget:[object HTMLDivElement]
//實際目標
target:[object HTMLDivElement]
//實際目標
srcElement:[object HTMLDivElement]

  四、eventPhase屬性返回一個整數值,表示事件目前所處的事件流階段。0表示事件沒有發生,1表示捕獲階段,2表示目標階段,3表示冒泡階段

  五、bubbles屬性返回一個布爾值,表示當前事件是否會冒泡。該屬性爲只讀屬性

  六、cancelable屬性返回一個布爾值,表示事件是否能夠取消。該屬性爲只讀屬性

//事件流
eventPhase: 2
//可冒泡
bubbles: true
//默認事件可取消
cancelable: true

【touchList】

  除了常見的DOM屬性外,觸摸事件對象有一個touchList數組屬性,其中包含了每一個觸摸點的信息。若是用戶使用四個手指觸摸屏幕,這個數組就會有四個元素。一共有三個這樣的數組

  一、touches:當前觸摸屏幕的觸摸點數組(至少有一個觸摸在事件目標元素上)

  二、changedTouches :致使觸摸事件被觸發的觸摸點數組

  三、targetTouches:事件目標元素上的觸摸點數組

  若是用戶最後一個手指離開屏幕觸發touchend事件,這最後一個觸摸點信息不會出如今targetTouches和touches數組中,可是會出如今changedTouched數組中。由於是它的離開觸發了touchend事件,因此changedTouches數組中仍然包含它。上面三個數組中,最經常使用的是changedTouches數組

    (function () {
      var
        elesMap = {
          touchObj: document.getElementById('test')
        },
        onTouch;
      onTouch = function (e) {
          elesMap.touchObj.innerHTML = 'touches:' + e.touches.length
                                    + '<br>changedTouches:' + e.changedTouches.length
                                    + '<br>targetTouches:' + e.targetTouches.length;
      };
      elesMap.touchObj.addEventListener('touchstart', function (event) { onTouch(event); });
    })(); 

【事件座標】

  上面這些觸摸點數組中的元素能夠像普通數組那樣用數字索引。數組中的元素包含了觸摸點的有用信息,尤爲是座標信息。每一個Touch對象包含下列屬性

clientx:觸摸目標在視口中的x座標
clientY:觸摸目標在視口中的y座標
identifier:標識觸摸的惟一ID
pageX:觸摸目標在頁面中的x座標(包含滾動)
pageY:觸摸目標在頁面中的y座標(包含滾動)
screenX:觸摸目標在屏幕中的x座標
screenY:觸摸目標在屏幕中的y座標
target:觸摸的DOM節點目標

  changedTouches數組中的第一個元素就是致使事件觸發的那個觸摸點對象(一般這個觸摸點數組不包含其餘對象)。這個觸摸點對象含有clientX/Y和pageX/Y座標信息。除此以外還有screenX/Y和x/y,這些座標在瀏覽器間不太一致,不建議使用

  clientX/Y和pageX/Y的區別在於前者相對於視覺視口的左上角,後者相對於佈局視口的左上角。佈局視口是能夠滾動的

    (function () {
      var
        elesMap = {
          touchObj: document.getElementById('test')
        },
        onTouch;
      onTouch = function (e) {
        var touch = e.changedTouches[0];
        elesMap.touchObj.innerHTML = 'clientX:' + touch.clientX + '<br>clientY:' + touch.clientY
          + '<br>pageX:' + touch.pageX + '<br>pageY:' + touch.pageY
          + '<br>screenX:' + touch.screenX + '<br>screenY:' + touch.screenY
      };
      elesMap.touchObj.addEventListener('touchstart', function (event) { onTouch(event); });
    })(); 
相關文章
相關標籤/搜索