JavaScript Browser Events 瀏覽器事件
在網頁開發中,當使用者與瀏覽器互動時會觸發各式各樣的「事件」(Events)。除了常見的點擊 (click) 或鍵盤 (keydown) 事件外,瀏覽器本身也提供了一系列與視窗狀態、頁面可見性及生命週期相關的事件。掌握這些事件,能讓你更精確地控制應用程式的資源消耗、處理數據儲存並提升使用者體驗。
常見的視窗事件 (Window Events)
這些事件通常綁定在 window 物件上,用來監測瀏覽器視窗整體的狀態變化。
| 事件名稱 | 觸發時機 |
|---|---|
load | 網頁所有資源(包含圖片、樣式表等)都載入完成時觸發。 |
resize | 瀏覽器視窗大小被改變時觸發。 |
scroll | 使用者捲動網頁時觸發。 |
beforeunload | 使用者準備離開頁面(分頁關閉、重新整理)前觸發,可用於提示使用者存檔。 |
unload | 頁面正在被卸載時觸發(此事件目前已不建議用於儲存數據)。 |
視窗事件應用範例
頁面載入完成後執行 init
window.addEventListener('load', (event) => {
console.log('頁面所有資源已完成載入');
// 執行初始化邏輯
});
監聽視窗大小改變 (resize)
window.addEventListener('resize', () => {
console.log(`視窗大小改變為: ${window.innerWidth} x ${window.innerHeight}`);
});
離開頁面前的確認提示
window.addEventListener('beforeunload', (event) => {
// 標準做法是調用 preventDefault 或設定 returnValue
event.preventDefault();
event.returnValue = ''; // 現代瀏覽器多數會顯示自定義的對話框
});
Page Visibility API (頁面可見性)
過去開發者很難判斷使用者是否正在看目前的網頁(例如切換到了別的分頁,或將瀏覽器最小化)。Page Visibility API 解決了這個問題,讓我們可以在頁面被隱藏時暫停耗資源的任務。
visibilitychange 事件
當頁面的可見狀態改變時,document 會觸發 visibilitychange 事件。你可以透過 document.visibilityState 來判斷當前狀態:
visible: 頁面內容至少部分可見。hidden: 頁面不可見(切換分頁、瀏覽器最小化、或手機螢幕關閉)。
範例:網頁隱藏時暫停影片播放
document.addEventListener('visibilitychange', function () {
const video = document.getElementById('myVideo');
if (document.visibilityState === 'hidden') {
console.log('頁面隱藏,暫停播放');
video.pause();
} else {
console.log('頁面恢復可見,繼續播放');
video.play();
}
});
visibilitychange 是目前公認最可靠的「使用者離開頁面」指標,適合用來在背景停止動畫、輪詢 (polling) 或發送最後的分析數據。Page Lifecycle API (頁面生命週期)
現代瀏覽器為了節省系統資源(記憶體、CPU、電池),會主動「凍結」(Freeze) 或「暫載」(Discard) 長時間待在背景的分頁。Page Lifecycle API 定義了一套標準化的生命週期狀態,讓開發者能安全地處理這些介入行為。
生命週期狀態 (States)
- Active: 頁面可見且擁有焦點 (Focus)。
- Passive: 頁面可見但沒有焦點(例如使用者正在看另一個視窗,但此網頁仍可見)。
- Hidden: 頁面不可見但尚未被凍結。
- Frozen: 瀏覽器暫停了頁面的定時器和任務。
- Terminated: 使用者主動關閉了分頁或導向新頁面。
- Discarded: 瀏覽器為了節省記憶體,完全卸載了頁面內容(但導覽列仍留着分頁,點擊後會重新載入)。
關鍵生命週期事件
freeze: 瀏覽器即將凍結頁面時觸發。此時應停止所有計時器並儲存任何未儲存的 UI 狀態。resume: 當凍結的頁面被重新導向(非重新載入)並恢復執行時觸發。pageshow: 頁面被載入時觸發。可以透過event.persisted判斷是否來自 BFCache(瀏覽器的前進/後退快取)。pagehide: 使用者導向新頁面時觸發。
瀏覽器支援度與相容性
Page Lifecycle API 的 freeze 與 resume 事件目前主要由 Chromium 系瀏覽器(Chrome, Edge, Opera)完整支援。
- Chrome/Edge/Opera: 完整支援
freeze與resume。 - Safari/Firefox: 目前仍不支援標準的
freeze與resume事件。
相較之下,pageshow 與 pagehide 事件則是所有現代瀏覽器(包含 Safari, Firefox, Chrome, Edge)都完整支援的標準事件。
- 全瀏覽器支援:
pageshow與pagehide自 2015 年起已成為跨瀏覽器的標準,是處理 BFCache(往返快取)最可靠的方式。 - 優勢: 使用這些事件取代舊有的
load或unload,能讓網頁更順利地進入 BFCache,提升使用者點擊「上一頁/下一頁」時的載入速度。
visibilitychange 以及 pageshow / pagehide,而非僅依賴 freeze。推薦的使用模式
瀏覽器建議開發者不要再依賴 unload 事件,因為它在現代移動端瀏覽器和 BFCache 機制下非常不可靠。
// 監測生命週期狀態變化的推薦寫法
const logStateChange = (state) => {
console.log('當前狀態:', state);
};
// 監聽凍結事件
window.addEventListener('freeze', () => {
logStateChange('Frozen');
// 在這裡儲存資料
});
// 監聽恢復事件
window.addEventListener('resume', () => {
logStateChange('Resumed');
});
// 使用 visibilitychange 作為主要的離開判斷
document.addEventListener('visibilitychange', () => {
logStateChange(document.visibilityState);
});
unload 事件,因為它會阻止瀏覽器的 BFCache 優化,導致使用者點擊「回上頁」時必須重新載入整頁,影響速度。實務應用:整合範例
下面是一個綜合範例,用於監測頁面從載入、切換分頁到被凍結的完整過程:
// 取得當前狀態的輔助函式
const getState = () => {
if (document.visibilityState === 'hidden') return 'hidden';
if (document.hasFocus()) return 'active';
return 'passive';
};
let currentState = getState();
const handleStateChange = () => {
const nextState = getState();
if (nextState !== currentState) {
console.log(`狀態改變: ${currentState} -> ${nextState}`);
currentState = nextState;
}
};
// 綁定一系列相關事件
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, handleStateChange, { capture: true });
});
window.addEventListener(
'freeze',
() => {
console.log('頁面即將被凍結 (Frozen)');
},
{ capture: true }
);
window.addEventListener(
'pagehide',
(event) => {
if (event.persisted) {
console.log('頁面進入 BFCache (等同於 Frozen)');
} else {
console.log('頁面即將卸載 (Terminated)');
}
},
{ capture: true }
);
透過這些事件,你可以讓網頁應用程式在節省資源的同時,仍能保持穩定且快速的響應能力。
現代技術趨勢與議題
隨著 Web 技術的發展,有些傳統事件的處理方式已被更高效的 API 取代。
網路狀態感應 (Online/Offline)
你可以監聽 online 和 offline 事件來判斷使用者的網路連線狀態,對於 PWA 或需要即時同步的應用非常有用。
window.addEventListener('online', () => console.log('網路已恢復連線'));
window.addEventListener('offline', () => console.log('網路已斷開'));
效能優化:捨棄 scroll/resize 轉向 Observer
傳統的 scroll 和 resize 事件在監聽時會頻繁觸發(High-frequency events),容易造成頁面卡頓。現代開發建議使用以下 API:
- Intersection Observer: 用於判斷元素是否進入視窗範圍(例如圖片懶加載、無限捲動),效能遠優於監聽
scroll。 - Resize Observer: 用於監聽特定 DOM 元素(而非整個視窗)的大小變化,效能優於
window.resize。
BFCache (往返快取) 的影響
現代瀏覽器為了實現「瞬間返回」,會將頁面完整狀態保留在記憶體中(即 BFCache)。這會導致當使用者點擊「回上頁」時,不會觸發 load 事件。
- 解決方案: 使用
pageshow事件,並檢查event.persisted是否為true來判斷頁面是否從快取中恢復。
相關閱讀: