週末開發了一個在 GitHub 中給 repo 增長自定義備註的 chrome 擴展。javascript
開發這個擴展的緣由是我在 GitHub 中所 star 的項目實在太多了(截止目前 671 個),有的項目過個幾天回來看就忘了爲何 star 了,有的輪子想找的時候發現忘記叫什麼了,這麼多一個個找實在浪費時間。因而我一直在想有這麼個工具,能夠自定義對 GitHub 中的項目進行備註,而後能夠根據備註進行搜索,因而便有了這個擴展。css
如需安裝體驗請點擊 GitHub Remarks 進行安裝,源碼移步 github-remarks(沒啥參考價值)。java
固然本文並非講這個擴展的製做過程,而是在這過程當中碰到的一個問題。jquery
以個人 GitHub 帳戶舉例,在 https://github.com/hanzichi?t... 頁面,我須要插入一些 dom,使得頁面以下:git
乍一想,彷佛很簡單,在這個頁面插入 content.js 就行,在 manifest.json 中增長配置:github
{ "matches": ["*://github.com/.*tabs=stars"], "js": ["content-scripts/repoDetail.js"], "css": [], "run_at": "document_end" }
可是問題來了,直接打開頁面 https://github.com/hanzichi?t... 的,可是那個頁面並無參照的 dom 結構(好把咱們須要的 dom 插入)。因此問題彷佛就轉爲了,在 pjax 頁面,如何可以監聽到頁面 url 的變化?web
首先一個很簡單的方案是,弄個定時器循環監聽,可是太耗費性能了,passchrome
由於頁面是 pjax 跳轉,因此 hashchange 這樣的事件天然也用不上;而 pushstate 事件是監聽瀏覽器前進後退的,因此也並不符合場景json
有個想法很好,重寫 pushState 事件(代碼來自 How to detect when HTML5's history.pushState() is called?):瀏覽器
(function(history){ var pushState = history.pushState; history.pushState = function(state) { if (typeof history.onpushstate == "function") { history.onpushstate({state: state}); } // ... whatever else you want to do // maybe call onhashchange e.handler return pushState.apply(history, arguments); } })(window.history);
可是由於 chrome 擴展和源代碼之間並不共享做用域,因此重寫的 history.pushState 方法在原來頁面其實並無改變,因此也沒什麼卵用
而後從 chrome API 入手,發現有個 chrome API 能夠檢測當前 url 改變的 tab(參考 [Chrome extension WebNavigation listener for hash change
](https://stackoverflow.com/que...):
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (changeInfo.url) { console.log('Tab %d got new URL: %s', tabId, changeInfo.url) } })
這段代碼是在 background.js 中,而後一旦偵測到 URL 變化,background.js 和 content.js 通訊告知便可。
可是,可是,也有問題,url 一改變確實能監聽到,可是 chrome 擴展須要在相應 dom ready 後才能做用(才能在以前 dom 的基礎上插入新的 dom),你沒法檢測到何時 dom 已經準備就緒了,因此在這裏加個定時器延遲做用是可行的,時間須要本身估算下
這個方法不是很優雅,最後想到,pjax 的過程當中,dom 確定有變化,因此監聽 dom 變化是否可行?答案是能夠的:
MutationObserver = window.MutationObserver || window.WebKitMutationObserver; var observer = new MutationObserver(function(mutations, observer) { let url = location.href let p = /.*\/\/github.com\/.*\?tab=stars.*/ if (p.test(url)) { initStarsPage() } }) observer.observe(document.getElementById('js-pjax-container'), { childList: true }) $(document).ready(() => { let url = location.href let p = /.*\/\/github.com\/.*\?tab=stars.*/ if (p.test(url)) { initStarsPage() } })
寫到最後,其實並非監聽了 URL,而是監聽了 dom 的變化。
最後的最後,意外在 so 上找到 這個答案 彷佛就是對應個人問題,大概看了下,除了我舉例的幾個方案外,其實我遺漏了最簡單的一個方案,既然頁面使用了 pjax,那麼直接監聽 pjax:complete 事件便可:
$(document).on('pjax:complete', function() { let url = location.href let p = /.*\/\/github.com\/.*\?tab=stars.*/ if (p.test(url)) { initStarsPage() } })
忽然讓人感受,有時候 "真相" 每每老是那麼簡單。
+ 鳥宿池邊樹,僧敲月下門 - 鳥宿池邊樹,僧推月下門