提升手持裝置點選速度之touch事件帶來的坑!

葉小釵發表於2013-10-27

前言

上週六,我將我們專案的click換成了tap事件,於是此事如夢魘一般折磨了我一星期!!!

經過我前仆後繼的努力,不計代價的犧牲,不斷的埋坑填坑,再埋坑的動作,最後悲傷的發現touch事件確實是個坑!

但是touch事件帶來的使用者感受提高對我們來說是一巨大進步,所以一些問題我們必須攻克,然在下已幾近黔驢技窮,最後使出了渾身解數以一噁心的手段暫時壓制其問題......

現在分享被折磨過程,希望對各位有所幫助

點選不起作用

我使用的原始碼不是最新的,zepto初始化時便為document.body繫結touchstart、touchmove、touchend事件

所以我們現在每一次在手機上的點選都會觸發一次touch事件,由此可能引起的BUG:

 每次手指觸屏都會觸發touchstart等事件,可能堵塞瀏覽器本身行為

由於我們是單頁應用,初始化會為body設定height為100%,就算進入列表頁或者其它,body頁高度不會增加,於是就會出現不可點選現象!

以去哪兒為例

由於其城市列表為absolute,其它dom皆不顯示從而導致body實際高度為0,那麼此時如果為城市繫結tap事件的話,那麼tap事件

在ios手機上不會有作用,電腦上有用,某些android無效

此問題較容易解決:

① 讓body可伸縮,跟著元素擴充套件(不易實現)

② 將事件繫結至document(推薦)

tap特有的點透現象

地球人都知道tap會出現點透現象,我這裡上一個程式碼給各位測試一番

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
 6     <style>
 7         #list { border: 1px solid black; position: absolute; top: 0px; left: 10px; width: 200px; height: 100px; }
 8         #d { border: 1px solid black; height: 300px; width: 100%; }
 9     </style>
10 </head>
11 <body>
12     <div id="d">
13         <input type="text" id="input" style="width: 80px; height: 200px;" />
14         <div id="list">
15         </div>
16     </div>
17 </body>
18 <script src="res/libs/zepto.js" type="text/javascript"></script>
19 <script type="text/javascript">
20     window.log = function (msg) {
21         console.log(msg);
22         var div = $('#myMsg');
23         if (!div[0]) div = $('<div id="myMsg"></div>')
24         $('#d').append(div);
25         div.click(function () {
26             div.html('');
27         });
28         div.append($('<div>' + msg + '</div>'));
29     }
30     var list = $('#list');
31     var d = $('#d');
32     var input = $('#input');
33 
34     input.tap(function (e) {
35         input.val(new Date().getTime());
36     });
37 
38     list.tap(function (e) {
39         list.hide();
40         setTimeout(function () {
41             list.show();
42         }, 1000);
43 
44     });
45 
46     d.tap(function () {
47         log('div tap');
48     });
49 </script>
50 </html>

這個頁面有三個元素

① 父容器div,我們為他繫結了一個tap事件,會列印文字

② 在上的div,我們為其繫結了一個tap事件,點選便消失

③ input,主要用於測試focus問題

現在開啟touch事件的情況下,我們點選上面的div,他會消失,於是:

div消失會觸發div(list)的tap事件

div消失會觸發input獲取焦點事件

可能導致的專案BUG

以上問題可能導致些什麼問題呢?

提示層一閃而過

我們可能會遇到這麼一個場景:

表單提交頁,使用者提交時如果資訊有誤,會彈出一個提示,並且為蒙版新增click的關閉事件

但是有tap在的情況效果就不一樣了,我們極有可能點選提交,彈出提示層,觸發蒙版點選事件,蒙版關閉!!!

input獲取焦點彈出鍵盤

我們可能遇到這種情況,我們在彈出層上做了一些操作後,點選彈出層關閉彈出層,但是下面有一個input(div有事件也行)

於是觸發了div事件,於是input獲取了焦點,某明奇妙的彈出來鍵盤!!!

BUG原因

以上問題是主要出現的問題,由此問題可能衍生出其它BUG,足以讓我們苦不堪言!!!

但是引起這些問題的原因是什麼呢???

先說冒泡

我們看zepto的touch事件原始碼,可以知道,我們一開始就將touchstart繫結到了document上,然後從e獲取當前點選元素

完了觸發touchend模擬tap等事件。

於是,首先我們想到了阻止冒泡是否可以解決問題:於是稍微修改下程式碼:

1 list.tap(function (e) {
2     list.hide();
3     setTimeout(function () {
4         list.show();
5     }, 1000);
6     e.stopPropagation();
7 });

事實證明,阻止冒泡後div(d)的tap事件不會被觸發,但是input的獲取焦點問題依舊不能解決

至於原因,我這裡也只能是猜測div(d)的觸發是由於冒泡引起,而就算阻止冒泡阻止瀏覽器預設操作也不能阻止input獲取焦點

神奇菊花解決問題

於是經過老夫不洩的努力,終於發現一個噁心的方法,可以暫緩此問題,他就是菊花!!!!

先上個程式碼:

 1 //該程式碼在zepto touch原始碼中
 2 forTap = $('#forTap');
 3 if(!forTap[0]) { forTap = $('<div id="forTap" style="background: black;color: White; display: none; 
 4 border-radius: 60px; position: absolute; z-index: 99999; width: 60px; height: 60px"></div>');
 5     $('body').append(forTap);
 6 }
 7 
 8 //touchstart
 9 var el = touch.el; touch.isShowTap = false
10 while(el[0].nodeName != 'BODY'){
11 if(el.attr('lazyTap')) {
12     touch.isShowTap = true;
13     break;
14     }
15 el = el.parent();
16 }
17 
18 //touchend
19 var event = $.Event('tap')
20 event.cancelTouch = cancelAll
21 
22 touch.el.trigger && touch.el.trigger(event)
23 
24 if(touch.isShowTap) {
25     forTap.css({
26         top: (e.changedTouches[0].pageY - 30) + 'px',
27         left: (e.changedTouches[0].pageX - 30) + 'px'
28     }) 
29     forTap.show();
30     setTimeout(function () {
31         forTap.hide();
32     }, 350);
33 }

大家注意看程式碼,我現在為我們需要延遲的元素加一個lazyTap的屬性

1 <div id="d">
2     <input type="text" id="input"  style =" width: 80px; height: 200px;"/>
3     <div id="list" lazyTap="true">
4     </div>
5 </div>

於是我們現在點選div(d)時候就會馬上有一朵菊花移動到他下面

 

大家看到那個黑色的菊花了麼???

這朵菊花會跟著被設定了lazyTap的屬性觸發了tap後便不會引發下面的事件,於是把菊花的背景去掉,他就是一個隱藏的菊花了。。。。。。

於是,到此為止吧!!!

結語

菊花帶來的問題也有很大多,第一個就是不專業,第二個就是如果為li設定了lazyTap屬性,那麼在Ul上面繫結的事件會不會冒泡上去,我也沒有驗證!!

但是,按道理應該被觸發,所以!!!如果您有什麼更好的解決方案,請務必留意!!!

 

相關文章