前言
扯淡
這個月比較倒黴,我送了女朋友一臺膝上型電腦作為生日禮物,結果15天一過電腦就壞了,悲劇的我還把電腦盒子給扔了!淘寶不給換更不給退
於是被女朋友臭罵了一過星期後,今天本來在公司有任務的,但是去修了個電腦(換主機板啊......),一蹉跎就快五點了,現在反正幹勁也不行,就寫篇部落格吧
需求
上週五的時候,確切說是週四晚上,老闆終於又遞上了一個神奇的需求
瀏覽器檢測手機是否安裝app
尼瑪,反正在我看來,這個需求夠奇葩的,當時我還一致認為不能完成,但是最後也居然想出了(抄出了)一個辦法,於是這裡拿出來與各位分享下
在此之前,我們繼續聊下上次遇到的tap點透問題
tap“點透”再探索
http://www.cnblogs.com/yexiaochai/p/3377900.html
http://www.cnblogs.com/yexiaochai/p/3391015.html
看過上面兩篇部落格的的朋友應該知道我們為什麼要使用tap事件替換click事件,然後大概知道tap會帶來哪些問題,以及我是如何解決這些問題的
雖然完整解決方案因為公司財產問題未拿出來,但是基本思路是有了,其核心就是蒙版遮蓋!
話說還頭,這個蒙版其實還是不太能讓人接受,這不我又請教了我一個同事,我們同事提出了一個屬性:
pointer-events屬性值詳解
- auto——效果和沒有定義pointer-events屬性相同,滑鼠不會穿透當前層。在SVG中,該值和visiblePainted的效果相同。
- none——元素不再是滑鼠事件的目標,滑鼠不再監聽當前層而去監聽下面的層中的元素。但是如果它的子元素設定了pointer-events為其它值,比如auto,滑鼠還是會監聽這個子元素的。
- 其它屬性值為SVG專用,這裡不再多介紹了。
這個屬性,通過前端觀察/和張鑫旭的部落格再深入瞭解一番過後發現,這個傢伙可以消除一個元素的滑鼠事件呢!!!自然也包括touchstart了,於是老夫感興趣了,便有了今天這個插曲
是否繼承
我們首先來看看這個屬性是否會被繼承:
http://sandbox.runjs.cn/show/teegz43u
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 <meta content="telephone=no" name="format-detection" /> 7 <meta name="apple-mobile-web-app-capable" content="yes" /> 8 <style> 9 div { display: block; border: 1px solid black; margin: 10px; padding: 10px; } 10 </style> 11 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 12 13 </head> 14 <body> 15 16 <div id="p1"> 17 p1 18 <div id="p1-1"> 19 p1-1 20 <div id="p1-1-1"> 21 p1-1-1 22 </div> 23 <div id="p1-1-2"> 24 p1-1-2 25 </div> 26 </div> 27 </div> 28 29 <div id="p2"> 30 p2 31 <div id="p2-1"> 32 2-1 33 <div id="p2-1-1"> 34 p2-1-1 35 </div> 36 <div id="p2-1-2"> 37 p2-1-2 38 </div> 39 </div> 40 </div> 41 42 </body> 43 <script type="text/javascript"> 44 $('div').click(function () { 45 alert($(this).attr('id')) 46 47 }) 48 </script> 49 </html>
首先,現在滑鼠點選會有一個冒泡的過程,所以會一次彈出提示框,現在我們將下面的p2的style加上我們的pointer-events屬性試試點選是否有效果
結果表明,設定了pointer-events: none; 後,該元素以及下面的元素的click事件全部沒有了
甚至是我們的input框都不會獲取焦點了!!!,所以這個傢伙確實夠厲害,但是不會取消事件冒泡
有了這個結論,我們上我們的重量級程式碼吧,這個程式碼請各位用手機測試
屬性解決點透
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 <meta content="telephone=no" name="format-detection" /> 7 <meta name="apple-mobile-web-app-capable" content="yes" /> 8 <style> 9 #list { display: block; position: absolute; top: 100px; left: 10px; width: 200px; height: 100px; } 10 div { display: block; border: 1px solid black; height: 500px; width: 100%; } 11 #input { width: 80px; height: 200px; display: block; } 12 </style> 13 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 14 </head> 15 <body> 16 <div id="list" style="background: gray;"> 17 </div> 18 <div id="wrapper"> 19 <div id="d"> 20 <input type="text" id="input" /> 21 </div> 22 </div> 23 </body> 24 <script type="text/javascript"> 25 var list = $('#list'); 26 var d = $('#d'); 27 var input = $('#input'); 28 input.tap(function (e) { 29 input.val(new Date().getTime()); 30 }); 31 list.tap(function (e) { 32 // $('input').css("pointer-events", "none"); 33 list.hide(); 34 setTimeout(function () { 35 list.show(); 36 // $('input').css("pointer-events", "auto"); 37 }, 1000); 38 }); 39 d.tap(function () { 40 d.append($('<p>div tap</p>')); 41 }); 42 43 </script> 44 </html>
http://sandbox.runjs.cn/show/wub3i7fr
這裡各位請使用手機訪問試試,現在這個網頁有幾個問題:
我點選灰色區域會將灰色區域隱藏,灰色區域消失,一秒後重現,於是可能發生兩個問題:
① 某些瀏覽器中後面的div tap事件會觸發
② input元素必定獲得焦點
div事件有些時候我們可以通過阻止冒泡處理,但是input這個問題基本不可調和,因為其獲得焦點彈出鍵盤十分惱火
元素我們是通過一個蒙版解決,先我們來看看是否可以給我們的容器wrapper加上一個css屬性解決呢???
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 <meta content="telephone=no" name="format-detection" /> 7 <meta name="apple-mobile-web-app-capable" content="yes" /> 8 <style> 9 #list { display: block; position: absolute; top: 100px; left: 10px; width: 200px; height: 100px; } 10 div { display: block; border: 1px solid black; height: 500px; width: 100%; } 11 #input { width: 80px; height: 200px; display: block; } 12 </style> 13 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 14 </head> 15 <body> 16 <div id="list" style="background: gray;"> 17 </div> 18 <div id="wrapper"> 19 <div id="d"> 20 <input type="text" id="input" /> 21 </div> 22 </div> 23 </body> 24 <script type="text/javascript"> 25 var list = $('#list'); 26 var d = $('#d'); 27 var input = $('#input'); 28 input.tap(function (e) { 29 input.val(new Date().getTime()); 30 }); 31 list.tap(function (e) { 32 $('#wrapper').css("pointer-events", "none"); 33 list.hide(); 34 setTimeout(function () { 35 $('#wrapper').css("pointer-events", "auto"); 36 }, 350) 37 setTimeout(function () { 38 list.show(); 39 }, 1000); 40 }); 41 d.tap(function () { 42 d.append($('<p>div tap</p>')); 43 }); 44 45 </script> 46 </html>
http://sandbox.runjs.cn/show/1wldtigt
可以看到,我們解決了點透的問題,所以有我有這些厲害的同事是很幸運的,有了這個方案,我們就可以在zepto中封裝我們的程式碼了
在tap點選後為容器標籤設定該屬性,350ms後取消即可,這樣可以最大程度的包裝程式碼不會修改
但是這個方案不是沒有問題:
最重要的是他依然有個時間閥值,經過我測試時350ms,意思是我使用tap事件後會有350ms的事件某些區域完全無法點選
那麼容器所佔區域小的話還可接受,如果所佔區域大的話就是噩夢,因為使用者的點選明顯會出現阻力
然後,畢竟會點透的情況不是多數,所以此方案仍有缺陷,最後還是得動一些其它力氣
後續
這次tap的研究先到這裡,我們我們有機會再繼續,所以回到我們今天的主題吧!!!
瀏覽器檢測手機是否安裝app
這個需求初次提出其實讓人感覺很為難,至少我是很為難的,因為壓根就找不到辦法嘛,經過週四晚上的糾結,基本就放棄了
第二天也給老大說搞不定,老大基本也仍為搞不定,但是這個需求是最大的老大提出的,所以跟進力度很強,這不是,過了沒多久就來了個vip(代表級別高......)
哥來了就寄出了法寶,先是一個國外的網站,然後就是我們傳說中的淘寶了
PS:說實話,雖然電腦的事情與淘寶無關,但是老夫現在對淘寶還是比較怨念的!!!!
淘寶不愧是業內技術領先的技術團隊,我們來看看他的網站:
其它不必關注,我們就看這個“立即開啟”!!!各位知道手機上這個立即開啟幹了什麼嗎?
這個傢伙不得了,如果按照了app 的話點選立即開啟就會開啟app,如果沒有按照的話居然跳向了市場連線
在某些時候這個對等與他有一個a標籤會根據需求而獲得不同的值!!!
但是,我們知道開啟app是安裝了app才會開啟的,不然就是個死連結,死連結必然打不開!於是帶著好奇帶著敬畏,我們開啟了淘寶的js庫
PS:淘寶壓縮混淆後js確實很小,而且沒有使用類庫哦
各位看官直接找到這個目錄吧,於是進去我們一步步跟進去:
① 點選點
雖然小生本領不行,但是讀程式碼還是入門的,所以國內一旦出了什麼心技術,基本很快就能普及,這就是國內的技術,不是做不出來,就是不知道做出了是什麼樣子
一旦你做出了,那麼我也能做出了,並且做的更好,所以我們缺乏創新啊......
我們找到了入口,於是進入install方法
② install
這群程式碼一目瞭然,讀到這裡,其實可以很輕易的猜測實現方案了!
③ 猜測實現方案
於是我就開始猜測,猜測的結果就是:
如果安裝app 的情況下,開啟連結會讓window失去焦點,於是清除了計時器
如果沒有安裝app計時器裡面的程式碼會執行,所以跳向了app市場
當然,最後發現一個問題:手機上網頁無法失去焦點,這只是我自己的判斷(無法失去焦點),所以最後也放棄了這個猜測
④ 陷入僵局
於是思路再次陷入僵局,任務不能實現,但是將淘寶程式碼搞下來,也無法實現,最後就開始以各種漫無目的,垃圾的辦法搞,終於不注意成功了一次!!!
無意義的成功是因為將定時器設定的很大......
⑤ 解決方案
最後發現了方案,安裝app 的情況下,網頁會進入後臺開啟app!!!
網頁進入後臺後會掛起js 的執行,但是這個期間有600-1000ms的時間js仍然會執行
淘寶執行的閥值是600,我們大概是900,所以一直在原地踏步了很久
這個網頁進入後臺卻成了解決問題的關鍵,於是新鮮程式碼出來了:
1 var log = function (msg) { 2 $('body').before('<div class="log">' + msg + '</div>'); 3 }; 4 var timeout, t = 1000, hasApp = true; 5 setTimeout(function () { 6 if (hasApp) { 7 log('安裝了app'); 8 $('#dl_app').hide(); 9 10 } else { 11 log('未安裝app'); 12 $('#dl_app').show(); 13 log('開始強制下載'); 14 forceDownload(); 15 } 16 }, 2000) 17 function testApp() { 18 var t1 = Date.now(); 19 var ifr = $('<iframe id="ifr"></iframe>') 20 ifr.attr('src', '您們app的協議'); 21 $('body').append(ifr); 22 timeout = setTimeout(function () { 23 try_to_open_app(t1); 24 }, t); 25 } 26 function try_to_open_app(t1) { 27 var t2 = Date.now(); 28 if (!t1 || t2 - t1 < t + 200) { 29 hasApp = false; 30 } 31 } 32 testApp();
將這段程式碼加入網站首頁1-2秒後hasApp就會告訴我們是否安裝了app,當然問題也很明顯:
缺陷
① 經測試,如果未安裝app的情況下,safari會給出一個alert類似的提示,老夫將它去不掉!!!
PS:如果各位知道怎麼去掉,請賜教
② 進入H5站點,如果安裝了app便會開啟app,這個是無法避免的
除了上面兩個較明顯的缺陷,其它還好了......
結語
我們今天的學習暫時到此,也不知道對各位有沒有用