在前端的移動Web開發中,有一部分事件只在移動端產生,如觸摸相關的事件。接下來給你們簡單總結一下移動端的事件。javascript
因爲移動端默認的佈局視口寬度是980像素,因此網頁文字很是小,爲了快速讓網頁還原到原來的大小,Safari最新引入了雙擊縮放功能:用戶雙擊手機頁面的時候,瀏覽器會智能的縮放當前頁面到原始大小。css
雙擊縮放的原理就是,當用戶click一次以後,瀏覽器會通過約300ms以後檢測是否再有一次click,若是有的話,就會縮放頁面。不然的話就是一個click事件。html
因爲雙擊縮放功能存在,click事件觸發就會有大約200~300ms的延遲。前端
因爲雙擊縮放的存在,pc端的dblclick事件也失效了。java
因爲移動端設備大都具有觸摸功能,因此移動端瀏覽器都引入了觸摸(touch)事件。node
touch相關的事件跟普通的其餘dom事件同樣使用,能夠直接用addEventListener來監聽和處理。git
最基本的touch事件包括4個事件:github
touchstart: 當在屏幕上按下手指時觸發web
touchmove: 當在屏幕上移動手指時觸發api
touchend: 當在屏幕上擡起手指時觸發
touchcancel 當一些更高級別的事件發生的時候(如電話接入或者彈出信息)會取消當前的touch操做,即觸發touchcancel。通常會在touchcancel時暫停遊戲、存檔等操做。
在不少狀況下,觸摸事件和鼠標事件會同時被觸發(目的是讓沒有對觸摸設備優化的代碼仍然能夠在觸摸設備上正常工做)。
由於雙擊縮放檢測的存在,在移動設備屏幕上點擊操做的事件執行順序:
touchstart(瞬間觸發) → touchend → click(200-300ms延遲)
若是你使用了觸摸事件,能夠調用 event.preventDefault()來阻止鼠標事件被觸發。
當用戶手指觸摸到的觸摸屏的時候觸發。事件對象的 target 就是touch 發生位置的那個元素。
<div> 點擊我! </div> <script> var box = document.querySelector("div"); box.addEventListener("touchstart", function (e) { console.log('touchstart'); }); </script>
當用戶在觸摸屏上移動觸點(手指)的時候,觸發這個事件。必定是先要觸發touchstart事件,再有可能觸發 touchmove 事件。
touchmove 事件的target 與最早觸發的 touchstart 的 target 保持一致。touchmove事件和鼠標的mousemove事件同樣都會屢次重複調用,因此,事件處理時不能有太多耗時操做。不一樣的設備,移動一樣的距離 touchmove 事件的觸發頻率是不一樣的。
注意:
- 即便手指移出了 原來的target 元素,則 touchmove 仍然會被一直觸發,並且 target 仍然是原來的 target 元素。
- touchmove事件會屢次重複觸發,因爲移動端計算資源寶貴,儘可能保證事件節流
<div> <p></p> </div> <script> var i = 1; var box = document.querySelector("div"); var p = document.querySelector("p"); box.addEventListener("touchmove", function (e){ p.innerHTML = e.target.tagName + ", " + i++; }) </script>
當用戶的手指擡起的時候,會觸發 touchend 事件。如何用戶的手指從觸屏設備的邊緣移出了觸屏設備,也會觸發 touchend 事件。
touchend 事件的 target 也是與 touchstart 的 target 一致,即便已經移出了元素。
當觸點因爲某些緣由被中斷時觸發。有幾種可能的緣由以下(具體的緣由根據不一樣的設備和瀏覽器有所不一樣):
touchcancel 事件通常用於保存現場數據。好比:正在玩遊戲,若是發生了 。touchcancel 事件,則應該把遊戲當前狀態相關的一些數據保存起來。
TouchEvent
是一類描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態變化的事件。這類事件用於描述一個或多個觸點,使開發者能夠檢測觸點的移動,觸點的增長和減小,等等。
每 個 Touch
對象表明一個觸點; 每一個觸點都由其位置,大小,形狀,壓力大小,和目標 element
描述。 TouchList
對象表明多個觸點的一個列表.
TouchEvent
的屬性繼承了 UIEvent
和 Event
。
屬性列表:
TouchEvent.changedTouches
: 一個 TouchList
對象,包含了表明全部從上一次觸摸事件到這次事件過程當中,狀態發生了改變的觸點的 Touch
對象。
TouchEvent.targetTouches
: 一個 TouchList
對象,是包含了以下觸點的 Touch
對象:觸摸起始於當前事件的目標 element
上,而且仍然沒有離開觸摸平面的觸點。
TouchEvent.touches
: 一 個 TouchList
對象,包含了全部當前接觸觸摸平面的觸點的 Touch
對象,不管它們的起始於哪一個 element
上,也不管它們狀態是否發生了變化。
<style> .box { width: 100px; height: 100px; border: 1px solid #09c; background-color: #0dc; } </style> <div class="box"></div> <script> window.onload = function() { var box = document.querySelector('.box'); box.addEventListener('touchstart', function(e) { console.dir(e); // 查看TouchEvent對象的屬性和方法 }); } </script>
一個TouchList
表明一個觸摸屏幕上全部觸點的列表。
舉例來說, 若是一個用戶用三根手指接觸屏幕(或者觸控板), 與之相關的TouchList
對於每根手指都會生成一個 Touch
對象, 共計 3 個.
只讀屬性:length
返回這個TouchList
中Touch
對的個數。(就是有幾個手指接觸到了屏幕)
方法:item(index)
返回TouchList
中指定索引的Touch
對象。
<div> <p style="font-size: 50px; color: #ffffff;"></p> </div> <script> var box = document.querySelector("div"); var p = document.querySelector("p"); box.addEventListener("touchend", function (e){ p.innerHTML = e.changedTouches.length; //返回Touch對象的個數 for(var i = 0; i < e.changedTouches.length; i++){ //遍歷出來每一個Touch對象 console.log(e.changedTouches.item(i)); } }) </script>
測試多個手機觸摸屏幕:
<div></div> <p></p> <script> var div = document.querySelector("div"); var p = document.querySelector("p"); div.addEventListener("touchstart", function (e){ var msg = "touches.length: " + e.touches.length + "<br> targetTouches.length: " + e.targetTouches.length + "<br> changedTouches.length: " + e.changedTouches.length; p.innerHTML = msg; }) </script>
操做:
放1個手指在div上
div
上div
上 Touch
表示用戶和觸摸設備之間接觸時單獨的交互點(a single point of contact
)。 這個交互點一般是一個手指或者觸摸筆, 觸摸設備一般是觸摸屏或者觸摸板。
基本屬性列表(都是隻讀):
編號 | 屬性名 | 屬性說明 |
---|---|---|
1. | identifier |
表示每 1 個 Touch 對象 的獨一無二的 identifier 。有了這個 identifier 能夠確保你總能追蹤到這個 Touch 對象。 |
2. | screenX |
觸摸點相對於屏幕左邊緣的 x 座標。 |
3. | scre enY |
觸摸點相對於屏幕上邊緣的 y 座標。 |
4. | clientX |
觸摸點相對於瀏覽器的 viewport 左邊緣的 x 座標。不會包括左邊的滾動距離。 |
5. | clientY |
觸摸點相對於瀏覽器的 viewport 上邊緣的 y 座標。不會包括上邊的滾動距離。 |
6. | pageX |
觸摸點相對於 document 的左邊緣的 x 座標。 與 clientX 不一樣的是,他包括左邊滾動的距離,若是有的話。 |
7. | pageY |
觸摸點相對於 document 的左邊緣的 y 座標。 與 clientY 不一樣的是,他包括上邊滾動的距離,若是有的話。 |
8. | target |
老是表示 手指最開始放在觸摸設備上的觸發點所在位置的 element 。 即便已經移出了元素甚至移出了document , 他表示的element 仍然不變 |
案例:
var box = document.querySelector("div"); var p = document.querySelector("p"); box.ontouchstart = function (e){ var touchList = e.changedTouches; for (var i = 0; i < touchList.length; i++){ var touch = touchList[i]; var msg = `id : ${touch.identifier} <br> screenX : ${touch.screenX} <br> screenY : ${touch.screenY} <br> clientX : ${touch.clientX} <br> clientY : ${touch.clientY} <br> pageX : ${touch.pageX} <br> pageY : ${touch.pageY} <br> target: ${touch.target.nodeName} <br> `; p.innerHTML = msg; } }
沒有左右滾動:
左右滾動:pageX
明顯大於 clientX
因爲點擊事件常用,若是用click會有延遲問題,通常咱們會用touch事件模擬移動端的點擊事件, 如下是封裝的幾個事件,僅供參考。
(function (window){ //傳入window,提升變量的查找效率 function myQuery(selector){ //這個函數就是對外提供的接口。 //調用這個函數的原型對象上的_init方法,並返回 return myQuery.prototype._init(selector); } myQuery.prototype = { /*初始化方法,獲取當前query對象的方法*/ _init: function (selector){ if (typeof selector == "string"){ //把查找到的元素存入到這個原型對象上。 this.ele = window.document.querySelector(selector); //返回值其實就是原型對象。 return this; } }, /*單擊事件: * 爲了規避click的300ms的延遲,自定義一個單擊事件 * 觸發時間: * 當擡起手指的時候觸發 * 須要判斷手指落下和手指擡起的事件間隔,若是小於500ms表示單擊時間。 * 若是是大於等於500ms,算是長按時間 * */ tap: function (handler){ this.ele.addEventListener("touchstart", touchFn); this.ele.addEventListener("touchend", touchFn); var startTime, endTime; function touchFn(e){ e.preventDefault() switch (e.type){ case "touchstart": startTime = new Date().getTime(); break; case "touchend": endTime = new Date().getTime(); if (endTime - startTime < 500){ handler.call(this, e); } break; } } }, /** * 長按 * @param handler */ longTag: function (handler){ this.ele.addEventListener("touchstart", touchFn); this.ele.addEventListener("touchmove", touchFn); this.ele.addEventListener("touchend", touchFn); var timerId; function touchFn(e){ switch (e.type){ case "touchstart" : //500ms以後執行 timerId = setTimeout(function (){ handler.call(this, e); }, 500) break; case "touchmove" : //若是中間有移動也清除定時器 clearTimeout(timerId) break; case "touchend" : //若是在500ms以內擡起了手指,則須要定時器 clearTimeout(timerId); break; } } }, /** * 左側滑動。 * 記錄手指按下的左邊,在離開的時候計算 deltaX是否知足左滑的條件 */ slideLeft: function (handler){ this.ele.addEventListener("touchstart", touchFn); this.ele.addEventListener("touchend", touchFn); var startX, startY, endX, endY; function touchFn(e){ e.preventDefault(); var firstTouch = e.changedTouches[0]; switch (e.type){ case "touchstart": startX = firstTouch.pageX; startY = firstTouch.pageY; break; case "touchend": endX = firstTouch.pageX; endY = firstTouch.pageY; //x方向移動大於y方向的移動,而且x方向的移動大於25個像素,表示在向左側滑動 if (Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX >= 25){ handler.call(this, e); } break; } } }, /* 右側滑動 */ rightLeft: function (e){ //TODO: } } window.$ = window.myQuery = myQuery; })(window); // ======================== // 使用: $("div").tap(function (e){ console.log("單擊事件") }) $("div").longTag(function (){ console.log("長按事件"); }) $("div").slideLeft(function (e){ console.log(this); this.innerHTML = "左側滑動了....." })
手勢相關的事件通常就是tap類(觸屏)和滑動(swipe)事件兩類。都是基於原生的touchstart、touchmove、touchend事件,封裝成不一樣的手勢類型自定義事件。
觸碰事件,我目前還不知道它和touch的區別,通常用於代替click事件,有tap longTap singleTap doubleTap四種之分。
滑動事件,有swipe swipeLeft swipeRight swipeUp swipeDown 五種之分。
Zepto.js 是一個輕量級的針對現代高級瀏覽器的JavaScript庫, 它適配了jQuery的大部分api,也就是jQuery怎麼用,Zepto.js就怎麼用。它很是小,很是適合移動端。
Zepto.js的touch模塊中封裝了手勢相關的代碼。封裝了再觸摸設備上觸發tap– 和 swipe– 相關事件,也適用於全部的touch
(iOS, Android)和pointer
事件(Windows Phone)。
<style>.delete { display: none; }</style> <ul id=items> <li>List item 1 <span class=delete>DELETE</span></li> <li>List item 2 <span class=delete>DELETE</span></li> </ul> <script> $('#items li').swipe(function(){ $('.delete').hide() $('.delete', this).show() }) $('.delete').tap(function(){ $(this).parent('li').remove() }) </script>
hammer.js
hammer提供了不只僅tap、swipe等事件,還提供了:pan(平移)、pinch類(捏拿縮放)、 press類(按住)、 rotate類(旋轉)類手勢支持, hammer.js詳解教程
若是某個返回按鈕的位置,剛好在要返回的這個頁面的帶有href屬性的a標籤的範圍內,在點擊返回按鈕後,頁面快速切換到有a標籤的頁面,300ms後觸發了click事件,從而觸發了a標籤的意外跳轉,這個就是典型的點擊穿透問題。罪魁禍首其實就是a標籤跳轉默認是click事件觸發,而移動端的touch事件觸發以後,依然會在300ms後觸發click事件。
解決辦法:
1.就是阻止觸發touch事件完成後的click事件。
2.不要混用touch和click事件。顯然不可能都綁定click事件,由於要解決300ms延遲問題(除了fastclick),那麼只能都綁定touch事件,這樣click事件永遠不會被觸發。
注意:zepto並無阻止click事件,因此使用zepto的tap事件依然會致使點擊穿透問題,你須要手動添加 e.preventDefault() 來阻止click事件。
參考文章: