若是你關注我應該知道,我最近對PC端頁面進行移動適配。在這個過程當中,爲了節省用戶300ms的時間,同時給予用戶更及時的點擊反饋(這意味着更好的用戶體驗),我在嘗試使用移動端獨有的 touchstart
事件替代傳統的 click
事件,這過程當中我遇到了一些小問題,併成功解決了,你能夠經過這篇文章查看具體的狀況。html
所謂禍不單行,在即將發佈上線的時候,我又忽然發現使用 touchstart
事件後,移動設備上出現了另外一個比較詭異的現象:當用戶滾動頁面後,本來綁定在fixed定位的navbar元素上的 touchstart
事件會時常失效。你能夠經過掃描下方二維碼,並使用你的Safari瀏覽器或Chrome瀏覽器(注意:不是瀏覽器自帶的模擬器)親自感覺這一奇怪的現象。git
固然,最終我成功解決了這個問題,而且有意思的是,這個問題彷佛並不出自個人代碼,而被我歸咎因而瀏覽器的Bug。可是對於這個Bug出現的原理,我也只有一個大概的推測,若是你清楚的知道產生這一現象的緣由,也歡迎你和我分享。github
在本篇文章中,我不但會記錄個人解決方案,而且會記錄我在遇到這個問題後的debug的過程與分析思路。不過若是你正被這個問題搞得焦頭爛額,只想快點擺脫這個問題,你能夠直接翻閱到文章底部「解決方案」部分,參考個人解決方案(我真是貼心,對吧? ?)。segmentfault
當在移動設備上使用測試瀏覽器打開網頁並滾動屏幕後,會發現再次點擊navbar,navbar元素綁定的 touchstart
事件並無被觸發(或偶爾不被觸發)。瀏覽器
我是在無心中發現該問題的,當時觀察到的現象是綁定在navbar上的 touchstart
事件有時會被觸發,有時會失效。因而我使用Google搜索了「touchstart 事件偶爾失效」的關鍵字,很遺憾,並無什麼靠譜的答案。這說明「並不存在touchstart偶爾失效的問題」。也就是說,我須要找到確切的令 touchstart
響應事件失效的緣由。函數
接下來,我不斷的在移動設備上嘗試各類操做(雙擊,滾動屏幕,放大等),並留心移動設備的響應,最終將 touchstart
綁定事件失效的緣由肯定爲在「頁面滾動以後」。接下來的事很簡單,繼續Google搜索「touchstart 事件在頁面滾動後失效」。觀察首屏搜索結果,並點擊進去查看,遺憾的是,並無什麼合適的信息:測試
接着我抱着試試看的態度切換百度搜索一樣的關鍵詞,還不錯,已經有了一個類似度很高的搜索結果,可是閱讀後發現依然不是我想要的:搜索引擎
是時候上最終的大殺器了,使用英文關鍵字搜索!如下是我使用了「touch event doesn't respond after page scroll」關鍵字的Google搜索結果:spa
老樣子,仍是沒有使人滿意的結果。至此我得到了兩點信息:操作系統
touch
事件的相關概念上找緣由;至此,debug的第一階段結束,個人徒勞無功代表了這個問題並不簡單,我須要認真對待,接下來我採用瞭如下兩種方法:
思路很清晰對吧,可是我並無在相關文檔中找到可能引起此問題的任何靈感。不過好在我經過第一步已經讓問題變得很是清晰了,不要灰心,繼續思考。
基本上到了這個階段,debug的過程就進入到經驗和直覺領域了,要成功解決這個問題,你有時還須要一點點運氣,我在這個過程當中嘗試瞭如下方案:
「無腦試對」:我將在搜索引擎中看到的一些問題的解決方法,逐個試驗,但願有個會管用,我能夠得到更多信息去定位問題出現的緣由。這些嘗試有:爲事件的回調函數添加 e.preventDefault()
方法,替換 touchstart
事件爲 touchend
事件或者直接是 click
事件。
很尷尬,這些嘗試都沒有起到做用,問題依然存在,不過沒有關係,我原本也沒有對這個簡短的嘗試抱太大的指望,不過這其實也說明這個奇怪的現象和touch具體的事件類型無關,和touch事件誤觸發其他事件無關。
目前爲止,我已經知道了 touch
事件我使用的方式是正確的,而且沒有其他的因素能夠干擾點擊事件的觸發,天然而然的,我開始好奇,瀏覽器到底有沒有檢測到我手指的「點擊」。這能夠經過如下代碼獲得答案:
window.addEventListener("touchstart", e => { console.log(e.target) })
奇妙的事情發生了,個人navbar竟然再也不出現頁面滾動後touch事件失效的問題!可是當我按照相同的思路,將代碼替換爲下面的代碼想要看看返回值時:
var navbar = document.querySelector(".navbar") navbar.addEventListener("touchstart", e => { console.log(e.target) })
問題又出現了,而且當頁面滾動後,每當我再次點擊navbar,控制檯沒有任何輸出,這意味着瀏覽器認爲我並無點擊navbar!
這不科學,可是我已然看到勝利的曙光。當我我將原先綁定在navbar上的 touchstart
事件經過事件的冒泡機制綁定在 window
對象,經過判斷 e.target
屬性進行事件回調時 -- 問題解決了,頁面正常了,整個世界都清淨了...
代碼以下:
var navbar = document.querySelector(".navbar") window.addEventListener("touchstart", e => { if (e.target === navbar) { // callback } })
掃描二維碼查看正確的效果:
到此爲止問題被成功解決了嗎?並無。
雖然世界清淨了的那一刻使人神清氣爽,可是這只是需求被實現了,問題並無被解決,我指的是我內心的那個問題:「爲何這樣就行,而原來那樣就不行?」。這個問題相當重要,也但願大家不要忽略。
你贊成嗎?那讓咱們繼續。
讓咱們再回過頭分析一下咱們的代碼,很明顯它已經很是精簡了,惟一可能出問題的地方在於咱們給navbar的 fixed
定位。咱們再想一想咱們是怎麼「誤打誤撞」解決這個問題的,navbar檢測不到咱們的點擊,可是window能夠,將這兩個線索放在一塊兒思考,我得出了一個很值得懷疑的對象:層級。
我試着取消了navbar的 position: fixed;
聲明,果真,一切又都正常了。看來這一奇異現象的始做俑者就是這條聲明。而我能想到與之相關的因素就是層級,我是指DOM對象的層級。
最終我是這樣解釋這個奇怪現象產生的緣由:
在頁面初始化時,瀏覽器的DOM樹被正確的渲染,也就是說DOM元素間的關係正確,所以 navbar 元素能夠準確的捕捉咱們的 touchstart 事件,可是當頁面滾動後,瀏覽器丟失了 navbar 元素的層級關係, touchstart 事件沒法經過冒泡被 navbar 元素捕捉,所以咱們綁定的事件沒有響應。而當咱們讓整個window對象監測 touchstart 事件後,瀏覽器能夠從新正確的計算DOM對象間的關係,navbar 層能夠捕捉到冒泡的事件,所以一切就都正常了。
這個解釋有說服你嗎?其實我內心也沒個底,畢竟這只是我基於現象的一種推測。但不管如何,這種奇怪現象的發生,應該被歸咎於瀏覽器,而不是個人代碼(哈,鬆了一口氣)。
到這裏,這個問題就結束了嗎?並無。若是隻是憑一個現象,一個推測就甩鍋瀏覽器,會不會讓人有一種欽定的感受,讓某些人不服呢?會的,我本身心就比較虛,不過不要緊,只要掌握瞭如下的關鍵訣竅,甩鍋瀏覽器還不是分分鐘的事。
該訣竅就是 -- 你本身去多測幾個瀏覽器啊,朋友!!
咱們根據引擎區分不一樣瀏覽器,
因而我下載了Firefox瀏覽器從新測試了原代碼下頁面效果,果真沒有問題!
呵呵,不是我代碼的問題。都是使用Webkit引擎的瀏覽器很差 :)。
你覺得本篇文章就這麼結束了?並無(失望吧),實際上寫到這裏,你應該也有所感受,雖然此次debug成功解決了問題,但整個過程並不流暢高效,而且在其中走了些許彎路。結合本次debug的經驗,我總結了如下幾個在下次debug過程當中須要注意的方面:
debugger
關鍵字而不是 console.log
方法進行調試;(沒錯,debugger
關鍵字讓調試更高效);以上,是我在實際開發過程當中發現問題,分析問題並解決問題的過程和思路,但願對大家有幫助。