目前的 userscript 支援這四種啟動時機 (用 @run-at
參數指定):
- document-start (一開始就跑)
- document-body (出現 body 後跑)
- document-end (DOMContentLoaded 時,或是之後跑)
- document-idle (DOMContentLoaded 後跑)
但對於 SPA 類的頁面來說,即使用到 document-idle
,也不保證執行時頁面已經渲染完成,這時候可能是 framework 才正要開始處理頁面的時候。
如果我們的 userscript 想要「等」這些 framework 處理完後再開始跑,其中一種 workaround 是用 setTimeout()
等,但這樣很容易被 side effect 影響,像是電腦慢一點的時候還是會失敗,而如果 setTimeout()
時間拉太長體驗又不好:
setTimeout(() => { // ... }, 1000);
比較好的方式是用 MutationObserver()
聽事件,在每次有新元素插入時判斷是否達成條件,處理完成後再停止聽事件 (避免持續影響效能):
let observer = new MutationObserver(() => { // ... // observer.disconnect(); }); observer.observe(document.documentElement, { attributes: false, childList: true, subtree: true, });
有些 library 有把這段包起來,但看了使用方式覺得很複雜 (因為要支援比較多的情境),反而是自己把 MutationObserver()
的概念搞清楚後,用這幾行包起來還比較簡單...