我是如何經過debug成功甩鍋瀏覽器的:解決fixed定位元素,在頁面滾動後touch事件失效問題

若是你關注我應該知道,我最近對PC端頁面進行移動適配。在這個過程當中,爲了節省用戶300ms的時間,同時給予用戶更及時的點擊反饋(這意味着更好的用戶體驗),我在嘗試使用移動端獨有的 touchstart 事件替代傳統的 click 事件,這過程當中我遇到了一些小問題,併成功解決了,你能夠經過這篇文章查看具體的狀況。html

所謂禍不單行,在即將發佈上線的時候,我又忽然發現使用 touchstart 事件後,移動設備上出現了另外一個比較詭異的現象:當用戶滾動頁面後,本來綁定在fixed定位的navbar元素上的 touchstart 事件會時常失效。你能夠經過掃描下方二維碼,並使用你的Safari瀏覽器或Chrome瀏覽器(注意:不是瀏覽器自帶的模擬器)親自感覺這一奇怪的現象。git

clipboard.png

固然,最終我成功解決了這個問題,而且有意思的是,這個問題彷佛並不出自個人代碼,而被我歸咎因而瀏覽器的Bug。可是對於這個Bug出現的原理,我也只有一個大概的推測,若是你清楚的知道產生這一現象的緣由,也歡迎你和我分享。github

在本篇文章中,我不但會記錄個人解決方案,而且會記錄我在遇到這個問題後的debug的過程與分析思路。不過若是你正被這個問題搞得焦頭爛額,只想快點擺脫這個問題,你能夠直接翻閱到文章底部「解決方案」部分,參考個人解決方案(我真是貼心,對吧? ?)。segmentfault

一 · 問題描述

  • 移動設備:iPhone 6
  • 操做系統:iOS 11.2.5
  • 測試瀏覽器:Safari,Chrome

  • 點擊此處查看示例代碼

當在移動設備上使用測試瀏覽器打開網頁並滾動屏幕後,會發現再次點擊navbar,navbar元素綁定的 touchstart 事件並無被觸發(或偶爾不被觸發)。瀏覽器

二 · 分析排查

(一)步驟一:使用搜索引擎

我是在無心中發現該問題的,當時觀察到的現象是綁定在navbar上的 touchstart 事件有時會被觸發,有時會失效。因而我使用Google搜索了「touchstart 事件偶爾失效」的關鍵字,很遺憾,並無什麼靠譜的答案。這說明「並不存在touchstart偶爾失效的問題」。也就是說,我須要找到確切的令 touchstart 響應事件失效的緣由。函數

接下來,我不斷的在移動設備上嘗試各類操做(雙擊,滾動屏幕,放大等),並留心移動設備的響應,最終將 touchstart 綁定事件失效的緣由肯定爲在「頁面滾動以後」。接下來的事很簡單,繼續Google搜索「touchstart 事件在頁面滾動後失效」。觀察首屏搜索結果,並點擊進去查看,遺憾的是,並無什麼合適的信息:測試

clipboard.png

接着我抱着試試看的態度切換百度搜索一樣的關鍵詞,還不錯,已經有了一個類似度很高的搜索結果,可是閱讀後發現依然不是我想要的:搜索引擎

clipboard.png

是時候上最終的大殺器了,使用英文關鍵字搜索!如下是我使用了「touch event doesn't respond after page scroll」關鍵字的Google搜索結果:spa

clipboard.png

老樣子,仍是沒有使人滿意的結果。至此我得到了兩點信息:操作系統

  1. 多是個人代碼寫的有問題:由於很小可能出現奇怪的現象歷來沒有人遇到過;
  2. 我應該從 touch 事件的相關概念上找緣由;

(二)步驟二:隔離代碼,明晰概念

至此,debug的第一階段結束,個人徒勞無功代表了這個問題並不簡單,我須要認真對待,接下來我採用瞭如下兩種方法:

  1. 抽離核心代碼,經過隔離外界不肯定因素,排除外因;
  2. 查閱MDN上touch事件的文檔,查找可能引起此類問題的內因;

思路很清晰對吧,可是我並無在相關文檔中找到可能引起此問題的任何靈感。不過好在我經過第一步已經讓問題變得很是清晰了,不要灰心,繼續思考。


(三)步驟三:大膽假設,當心求證

基本上到了這個階段,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
    }
})

掃描二維碼查看正確的效果:

clipboard.png

到此爲止問題被成功解決了嗎?並無。

雖然世界清淨了的那一刻使人神清氣爽,可是這只是需求被實現了,問題並無被解決,我指的是我內心的那個問題:「爲何這樣就行,而原來那樣就不行?」。這個問題相當重要,也但願大家不要忽略。

你贊成嗎?那讓咱們繼續。

讓咱們再回過頭分析一下咱們的代碼,很明顯它已經很是精簡了,惟一可能出問題的地方在於咱們給navbar的 fixed 定位。咱們再想一想咱們是怎麼「誤打誤撞」解決這個問題的,navbar檢測不到咱們的點擊,可是window能夠,將這兩個線索放在一塊兒思考,我得出了一個很值得懷疑的對象:層級。

我試着取消了navbar的 position: fixed; 聲明,果真,一切又都正常了。看來這一奇異現象的始做俑者就是這條聲明。而我能想到與之相關的因素就是層級,我是指DOM對象的層級

最終我是這樣解釋這個奇怪現象產生的緣由:

在頁面初始化時,瀏覽器的DOM樹被正確的渲染,也就是說DOM元素間的關係正確,所以 navbar 元素能夠準確的捕捉咱們的 touchstart 事件,可是當頁面滾動後,瀏覽器丟失了 navbar 元素的層級關係, touchstart 事件沒法經過冒泡被 navbar 元素捕捉,所以咱們綁定的事件沒有響應。而當咱們讓整個window對象監測 touchstart 事件後,瀏覽器能夠從新正確的計算DOM對象間的關係,navbar 層能夠捕捉到冒泡的事件,所以一切就都正常了。

這個解釋有說服你嗎?其實我內心也沒個底,畢竟這只是我基於現象的一種推測。但不管如何,這種奇怪現象的發生,應該被歸咎於瀏覽器,而不是個人代碼(哈,鬆了一口氣)。

到這裏,這個問題就結束了嗎?並無。若是隻是憑一個現象,一個推測就甩鍋瀏覽器,會不會讓人有一種欽定的感受,讓某些人不服呢?會的,我本身心就比較虛,不過不要緊,只要掌握瞭如下的關鍵訣竅,甩鍋瀏覽器還不是分分鐘的事。

該訣竅就是 -- 你本身去多測幾個瀏覽器啊,朋友!!

咱們根據引擎區分不一樣瀏覽器,

  • 使用Webkit引擎的瀏覽器:Chrome,Safari
  • 使用Gecko引擎的瀏覽器:Firefox
  • 使用Presto引擎的瀏覽器:Opera

因而我下載了Firefox瀏覽器從新測試了原代碼下頁面效果,果真沒有問題!

呵呵,不是我代碼的問題。都是使用Webkit引擎的瀏覽器很差 :)。

三 · 總結

你覺得本篇文章就這麼結束了?並無(失望吧),實際上寫到這裏,你應該也有所感受,雖然此次debug成功解決了問題,但整個過程並不流暢高效,而且在其中走了些許彎路。結合本次debug的經驗,我總結了如下幾個在下次debug過程當中須要注意的方面:

  • 依次使用Google,Google(英文關鍵字),百度搜索引擎搜索問題關鍵字;
  • 對沒有搜索到結果的問題保持警戒(檢查本身的代碼);
  • 編寫Demo,隔離核心代碼,簡化分析背景;
  • 確保掌握相關技術知識點;
  • 首先使用各瀏覽器測試(這樣即可以儘早排除是不是瀏覽器Bug);
  • 儘早使用 debugger 關鍵字而不是 console.log 方法進行調試;(沒錯,debugger 關鍵字讓調試更高效);
  • 仔細觀察問題,大膽假設,當心求證;
  • 不要放棄,在解決需求後,要解決問題;
  • 若是有時間精力的話,將發現的問題和debug過程當中學到的知識總結一篇博客吧 :);

以上,是我在實際開發過程當中發現問題,分析問題並解決問題的過程和思路,但願對大家有幫助。

相關文章
相關標籤/搜索