jQuery 中有一對關於所有 AJAX Requests 開始及結束的事件 - ajaxStart
及 ajaxStop
,除非是完全靜態的頁面,不然一個引用了 jQuery 的網頁或多或少都會去用它的 $.ajax()
來呼叫 Web Api,那麼這一對事件一定會被觸發,我們剛好可以利用來做一個頁面的載入等待機制。
相信各位朋友多少有看過網頁的載入動畫,像是 JetBrains 的 Issues Tracking 網頁它就有做載入動畫。
載入等待的機制,顧名思義,就是在資料完成載入或是頁面完成渲染之前,暫時顯示一個等待畫面,主要是用來提醒使用者稍候一會兒,也避免讓使用者看到一些破碎的資料或是頁面渲染的過程,以免降低使用者對網頁體驗的好感度。
ajaxStop
一個網頁裡面通常不會只呼叫一個 Web Api,而是一次呼叫多個,每個 Web Api 回應的時間都不一樣,所以我們要有一個機制知道一批 AJAX Requests 當中,最後一個結束的時間,而 jQuery 的 ajaxStop 就是拿來做這件事的。
所以我們想像上,頁面的載入等待機制的流程大致上會是這樣:
- 頁面初始時顯示「載入中」的訊息
- 註冊 ajaxStop 事件
- ajaxStop 事件被觸發時,取消註冊 ajaxStop 事件,並且隱藏「載入中」的訊息,顯示資料內容。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Loading - Test</title>
</head>
<body>
<div id="loading">載入中...</div>
<div id="dataArea" style="display: none">
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function () {
$(document).ajaxStop(function () {
$(document).off("ajaxStop");
$("#loading").hide();
$("#dataArea").show();
});
$.ajax("/test-data").then(function (data) {
data.forEach(function (item) {
$("#dataArea table tbody").append(`<tr><td>${item.id}</td><td>${item.name}</td></tr>`);
});
});
});
</script>
</body>
</html>
ajaxStart
但是,一個網頁當中呼叫的 Web Api 有可能是一批接著一批,這時候 ajaxStop 事件可能就會被重複觸發,導致資料內容還沒完全到位的時候,就顯示了出來。
要解決這個問題,我們可以用 ajaxStart 搭配 setTimeout/clearTimeout 來處理,大致上的流程是這樣:
- 頁面初始時顯示「載入中」的訊息
- 註冊 ajaxStart 事件(ajaxStart 事件會在一批 AJAX Requests 當中的第一個開始時被觸發)
- ajaxStart 事件被觸發時,clearTimeout。
- 註冊 ajaxStop 事件
- ajaxStop 事件被觸發時,setTimeout 執行取消註冊 ajaxStop 及 ajaxStart 事件,並且隱藏「載入中」的訊息,顯示資料內容。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Loading - Test</title>
</head>
<body>
<div id="loading">載入中...</div>
<div id="dataArea" style="display: none">
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function () {
var timeout;
var createTimeout = function () {
return setTimeout(function () {
$(document).off("ajaxStop");
$(document).off("ajaxStart");
$("#loading").hide();
$("#dataArea").show();
}, 100);
}
$(document).ajaxStart(function () {
if (timeout) {
clearTimeout(timeout);
}
});
$(document).ajaxStop(function () {
timeout = createTimeout();
});
$.ajax("/pre-test-data").then(function () {
$.ajax("/test-data").then(function (data) {
data.forEach(function (item) {
$("#dataArea table tbody").append(`<tr><td>${item.id}</td><td>${item.name}</td></tr>`);
});
});
});
});
</script>
</body>
</html>
大概看起來的效果就會像是下面這樣,剩下的就是請網頁美編幫我們弄一個像樣一點的畫面,讓它看起來不要那麼陽春。
以上,jQuery 的 ajaxStart 及 ajaxStop 事件的應用方式就分享給大家,希望對大家能起到一點點的幫助。
參考資料
- Detect when all AJAX Requests are Complete – jQuery
- .ajaxStop() | jQuery API Documentation
- .ajaxStart() | jQuery API Documentation