[小菜一碟] 用 jQuery 的 .ajaxStart() 及 .ajaxStop() 來製作一個頁面的載入等待機制

jQuery 中有一對關於所有 AJAX Requests 開始及結束的事件 - ajaxStartajaxStop,除非是完全靜態的頁面,不然一個引用了 jQuery 的網頁或多或少都會去用它的 $.ajax() 來呼叫 Web Api,那麼這一對事件一定會被觸發,我們剛好可以利用來做一個頁面的載入等待機制。

相信各位朋友多少有看過網頁的載入動畫,像是 JetBrains 的 Issues Tracking 網頁它就有做載入動畫。

載入等待的機制,顧名思義,就是在資料完成載入或是頁面完成渲染之前,暫時顯示一個等待畫面,主要是用來提醒使用者稍候一會兒,也避免讓使用者看到一些破碎的資料或是頁面渲染的過程,以免降低使用者對網頁體驗的好感度。

ajaxStop

一個網頁裡面通常不會只呼叫一個 Web Api,而是一次呼叫多個,每個 Web Api 回應的時間都不一樣,所以我們要有一個機制知道一批 AJAX Requests 當中,最後一個結束的時間,而 jQuery 的 ajaxStop 就是拿來做這件事的。

所以我們想像上,頁面的載入等待機制的流程大致上會是這樣:

  1. 頁面初始時顯示「載入中」的訊息
  2. 註冊 ajaxStop 事件
  3. 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 來處理,大致上的流程是這樣:

  1. 頁面初始時顯示「載入中」的訊息
  2. 註冊 ajaxStart 事件(ajaxStart 事件會在一批 AJAX Requests 當中的第一個開始時被觸發)
  3. ajaxStart 事件被觸發時,clearTimeout。
  4. 註冊 ajaxStop 事件
  5. 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 事件的應用方式就分享給大家,希望對大家能起到一點點的幫助。

參考資料

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學