window.history
透過 window.history 可以來控制上下頁 (back/forward) 的一些邏輯,使用 JS 來做一般的換頁或自訂的 history entry
back/forward/go
就如字面意思,history.back()
上一頁、history.forward()
下一頁,而 history.go(n)
則是往下 n 頁
history.go(1) // 下一頁,即 history.forward()
history.go(2) // 下二頁
history.go(-1) // 上一頁,即 history.back()
history.go(0) // 重整,或 history.go()
length
history.length
包含前 n 頁及後 m 頁及當前頁總共有幾個 history entry
scrollRestoration
auto
換頁時會自動記住當前的滾輪位置,跳回來時自動滾回到同個位置
history.scrollRestoration = 'auto'
manual
反之,不幫你記,換頁時捲軸一律在最上面,剩下的你自己處理
history.scrollRestoration = 'manual'
state
附加在 history entry 的資訊,一般來說會是 null,可以透過 pushState、replaceState 自行處理 history 邏輯並附帶 state
history.state
pushState
往下一頁至一個自訂的 history entry
history.pushState(stateObj, title [, url])
例如, history.pushState({ id: 'foo' }, 'title', '/foo')
就會產生一個新的 history entry 並帶有 state 為 { id: 'foo' }
,以及瀏覽器網址為 originalDomain.com/foo
,但瀏覽器並不會去抓取 /foo
的檔案或是確認此路徑是否存在
replaceState
與 pushState 類似,但是把當前 history entry override 成自訂的 entry
history.replaceState(stateObj, title, [url])
popstate event
在 window 中有個 popstate
的 event 可以來監聽上下頁 (history entry changed)
window.addEventListener('popstate', (e) => {
console.log(e.state)
})
A
popstate
event is dispatched to the window every time the active history entry changes between two history entries for the same document. — MDN
要注意的是,觸發條件為按下瀏覽器的上下頁或透過 history back/forward/go 且前往的頁面是同個 document,簡單一點就是曾經有呼叫過 pushState 或 replaceState 或使用 hash anchor #
window.addEventListener('popstate', (e) => {
console.log(e.state)
})history.pushState({ id: 1 }, '', '?foo=1')
history.pushState({ id: 2}, '', '?foo=2')
history.replaceState({ id: 3}, '')
location.href = '#foo'
history.pushState({ id: 4}, '', '?foo=4')history.back() // popstate event fired, logs null
history.back() // popstate event fired, logs { id: 3 }
history.back() // popstate event fired, logs { id: 1}
history.back() // popstate event fired, logs null
history.back() // navigate to prev document
Use case
在這次解 bug 之旅才發現許多網站都會使用 history 來管理換頁邏輯或是塞一些頁面資訊在 history state,那透過 pushState、replaceState、popstate event 可以做哪些應用呢
在這之前可以先裝個 userscript 以更方便觀察 history state 變化以及 pushState、replaceState 被使用的情形
https://gist.github.com/x3388638/91d3b70ccc4d9419155df95b96683844
換網址
在 load more 時換網址,以便使用者複製網址或重整時回到正確位置
像是在有 infinite scroll 功能的搜尋清單透過 replaceState 來更新 URL 到相應的頁數
或是一帶多的文章頁,往下捲一捲會捲到其他篇文章,一樣透過 replaceState 來確保 URL 跟當前畫面顯示的文章相符
SPA
利用 replaceState、pushState 記錄頁面資訊及換頁,再透過 popstate 取得 state 來做對應 history entry 的處理、render component
Ref
以上記錄一下觀察到的東西,但沒什麼實際經驗也不保證內容正確,且在不同瀏覽器上的行為可能不盡相同,僅供參考