提問: 我希望使用者按下瀏覽器的回上一頁或是重新整理時,能夠先跳出一個確認訊息,提醒使用者之後再讓他選擇是否離開該怎麼做?
我們可以利用Window History Event
中的 popstate 和 beforeunload搭配History API
來做到這件事情。
以 Angular
為例,我們可以透過 @HostListener
監聽 popstate
和 beforeunload
事件。
首先討論 popstate
,監聽 popstate
主要目的是為了抓取使用者透過回上一頁、前往下頁或是透過 history.pushState
或 history.popState
觸發的頁面切換。
/**
* 監聽瀏覽器的回上一頁
*/
@HostListener('window:popstate', ['$event'])
onPopState(event: PopStateEvent): void {
if (!window.confirm('要回到上一頁嗎?系統可能不會儲存您所做的變更')) {
history.go(1);
}
}
收到 popstate
事件後,透過 window
開啟了 confirm
窗口。如果使用者的回覆是取消的話,就透過 History API
前往下一個瀏覽紀錄。為什麼要前往下一個紀錄呢?
因為 popstate
事件的主要目的是讓開發者能夠回應歷史記錄的變化,而不是防止這些變化。這意味著一旦用戶決定導航到前一頁或下一頁,事件就會自動觸發。
並且,瀏覽器的歷史導航功能是為了提升用戶體驗,允許用戶在他們的瀏覽歷史中自由地前進和後退,因此在 popstate
事件中並沒有提供任何直接的方法來取消或阻擋這個事件。
於是我們雖然可以在監聽到 popstate
並且在觸發當下透過彈窗暫定程式執行,但無論使用者怎麼選擇,其實都已經回到前一頁了。所以才會有 history.go(1)
的部分。
接著是 beforeunload
的部分,監聽 beforeunload
的目的是為了攔截使用者透過重新整理、關閉分頁、輸入網址直接導頁等方式離開頁面。
/**
* 監聽瀏覽器的重新載入和關閉分頁
*/
@HostListener('window:beforeunload', ['$event'])
onBeforeunload(event: BeforeUnloadEvent): void {
event.preventDefault();
event.returnValue = '';
}
收到 beforeunload
事件後,我們透過 preventDefault
來阻止默認的行為,並且透過 event.returnValue
設置返回值來觸發瀏覽器顯示提示訊息,提醒使用者即將離開頁面。
透過上述的兩項監聽,即可做到在使用者離開頁面時跳出彈窗提醒使用者,並在使用者確認要離開後才離開,按下取消則回停留在當前頁面。特別注意的是,popstate
是針對離開當前頁面再回來的,所以若有資料或狀態,記得搭配快取進行處理。
下方補充一下,實作此功能所用到的相關知識科普,總共有瀏覽器的路由機制、Window
的 History events
和 History API
。
在瀏覽器中,從網址輸入之後到最終顯示網頁會經過很多階段,大致如下:
- URL 輸入:
用戶在瀏覽器的地址欄輸入 URL,並按下 Enter 鍵。- DNS 查詢:
瀏覽器會檢查本地緩存中是否有對應的 IP 地址。如果沒有,則發送 DNS 查詢請求到 DNS 伺服器,尋找對應的 > IP 地址。- 建立 TCP 連接:
瀏覽器與伺服器之間建立 TCP 連接,這通常使用三次握手(Three-way Handshake)進行。這個過程涉及 > > SYN、SYN-ACK 和 ACK 三個步驟。- 發送 HTTP 請求:
TCP 連接建立後,瀏覽器會向伺服器發送 HTTP 請求,請求特定的網頁資源。- 伺服器響應:
伺服器接收到請求後,處理請求並返回 HTTP 響應,包括狀態碼(例如 200 表示成功)和請求的資源(例如 HTML 文件)。- 內容渲染:
瀏覽器接收到響應後,開始解析 HTML 內容。這包括解析 CSS 和 JavaScript 代碼,以及加載圖像和其他資源。- DOM 和 CSSOM 建立:
瀏覽器會根據 HTML 內容構建文檔物件模型(DOM)和 CSS 物件模型(CSSOM)。- 渲染和顯示:
在完成 DOM 和 CSSOM 的構建後,瀏覽器會進行佈局計算,將內容渲染到螢幕上。並且在這些階段中,有些階段會觸發 Window Event,而我們則可以監聽這些 Event 作為判斷使用者是否正要離開或是回上一頁的依據。
主要有以下項目:
hashchange
觸發情況:當 URL 的 hash(# 後的部分)改變時觸發。
用途:用於處理基於 hash 的變化,適合用於 SPA(單頁應用程式),可用於更新頁面內容而無需重載整個頁面。popstate
觸發情況:當用戶使用瀏覽器的「返回」或「前進」按鈕導航到歷史記錄中的不同條目時觸發。
用途:用於根據當前歷史狀態更新頁面內容,特別是在使用 history.pushState() 或 history.replaceState() 方法修改歷史狀態時。beforeunload
觸發情況:當用戶嘗試離開當前頁面或重新加載頁面時觸發。
用途:可以用來顯示確認提示,防止用戶意外離開頁面,尤其是在未保存更改的情況下。unload
觸發情況:在文檔即將被卸載(離開或關閉)時觸發。
用途:用於清理工作,如關閉網路連接或清除定時器。注意,在這個事件中執行的代碼可能會有時間限制。load
觸發情況:當整個頁面(包括所有依賴的資源如樣式表和圖片)完全加載後觸發。
用途:通常用於執行需要等到所有資源都加載完成的代碼,如初始化應用或顯示內容。pageshow
觸發情況:當頁面被顯示(包括從歷史記錄恢復時)時觸發。
用途:用於管理頁面的顯示狀態,特別是在從歷史堆棧中返回時可以恢復頁面狀態。pagehide
觸發情況:當頁面即將被隱藏(如導航到另一個頁面)時觸發。
用途:可以用於清理或保存頁面狀態,以便在用戶返回時能夠恢復。pageswap
觸發情況:當頁面交換時觸發(此事件不是所有瀏覽器都支持,主要在特定框架中使用)。
用途:用於在 SPA 中管理頁面之間的切換,保持狀態的更新。
History API 是瀏覽器提供的一組方法,用於操作瀏覽器的歷史記錄,從而實現無需重新加載頁面即可更改 URL 的功能。主要的歷史紀錄相關方法包括:
history.pushState(state, title, url)
:
在歷史紀錄中推入新的狀態,並改變當前的 URL。
state 可以是任何數據對象,用於在不同狀態間傳遞數據。history.replaceState(state, title, url)
:
替換當前的歷史紀錄,而不創建新的歷史紀錄。history.go(num)
:
在歷史紀錄中移動指定數量的條目。
攔截回上一頁事件的功能到這邊就結束了,下一篇文章我們就來談談如何使用 Store 來實作存在 Angular 應用生命週期的快取機制吧!