基於h5的history改善ajax列表請求體驗_AJAX相關

來源:互聯網
上載者:User

資訊比較豐富的網站通常會以分頁顯示,在點“下一頁”時,很多網站都採用了動態請求的方式,避免頁面重新整理。雖然大家都是ajax,但是從一些小的細節還是 可以區分優劣。一個小的細節是能否支援瀏覽器“後退”和“前進“鍵。本文討論兩種方法,讓瀏覽器可以後退和前進,或者說讓ajax就像重新導向到新頁面一樣 擁有能夠返回到上一頁或者前進到下一頁。

      資料實現分頁顯示,最簡單的做法是在網址後面加多個page的當數,點“下一頁”時,讓網頁重新導向到page+1的新地址。例如新浪的新聞網就 是這麼做的,通過改變網址實現:index_1、index_2、index_3……。但是如果這個列表並不是頁面的主體部分,或者頁面的其它部分有很多 圖片等豐富元素,例如導航是一個很大的slider,再使用這樣的方式,整個頁面會閃爍得厲害,並且很多資源得重新載入。所以使用ajax請求,動態改變 DOM。

      但是普通的動態請求不會使網址發生變化,使用者點了下一頁,或者點了第幾頁,想要返回到上一個頁面時,可能會去點瀏覽器的返回鍵,這樣就導致返回的時候不是返回到原先查看的頁面了,而是上一個網址了。例如央視的新聞網就是這樣的。下面從ajax請求開始說起,以一個完整的案例進行分析。

      做了一個demo

   首先,寫一個請求:

 //當前第幾頁  var pageIndex = 0;  //請求函數  function makeRequest(pageIndex){    var request = new XMLHttpRequest();    request.onreadystatechange = stateChange;    //請求傳兩個參數,一個是當前第幾頁,另一個是每頁的資料條數    request.open("GET", "/getBook?page=" + pageIndex + "&limit=4", true);    request.send(null);    function stateChange(){      //狀態代碼為4,表示loaded,請求完成      if(request.readyState !== 4 ){        return;      }      //請求成功      if(request.status >= 200 && request.status < 300 || request.status === 304){        var books = JSON.parse(request.responseText);        renderPage(books);       }    }  }

     拿到資料後進行渲染:

  function renderPage(books){    var bookHtml =       "<table>" +      "  <tr>" +      "    <th>書名</th>" +      "    <th>作者</th>" +      "    <th>版本</th>" +      "  </tr>";    for(var i in books){      bookHtml +=         "<tr>" +        "  <td>" + books[i].book_name + "</td>" +        "  <td>" + books[i].author + "</td>" +        "  <td>" + books[i].edition + "</td>" +        "</tr>";    }    bookHtml += "</table>";    bookHtml +=       "<button>上一頁</button>" +       "<button onclick='nextPage();'>下一頁</button>";    var section = document.createElement("section");    section.innerHtml = bookHtml;    document.getElementById("book").appendChild(section);   }

這樣一個基本的ajax請求就搭起來了,然後再響應“下一頁”按鈕:

  function nextPage(){    //將頁面的index加1    pageIndex++;    //重新發請求和頁面載入    makeRequest(pageIndex);  }

到此,如果不做任何處理的話,就不能夠發揮瀏覽器返回、前進按鈕的作用。

      如果能夠檢測使用者點了後退、前進按鈕的話,就可以做些文章。h5就是增加了這麼一個事件window.onpopstate,當使用者點擊那兩個按鈕就會觸 發這個事件。但是光檢測到這個事件是不夠的,還得能夠傳些參數,也就是說返回到之前那個頁面的時候得知道那個頁面的pageIndex。通過 history的pushState方法可以達到這個目的,pushState(pageIndex)將當前頁的pageIndex存起來,再返回到這個 頁面時擷取到這個pageIndex。pushState的參數如下:

複製代碼 代碼如下:

window.history.pushState(state, title, url);

      其中state為一個object{},用來存放當前頁面的資料,title標題沒有多大的作用,url為當前頁面的url,一旦更改了這個url,瀏覽器地址欄的地址也會跟著變化。

      於是,在請求下一頁資料的nextPage函數裡面,加多一步操作:

  function nextPage(){    pageIndex++;    makeRequest(pageIndex);    //存放當前頁面的資料    window.history.pushState({page: pageIndex}, null, window.location.href);   }

然後監聽popstate事件:

  //如果使用者點擊返回或者前進按鈕  window.addEventListener("popstate", function(event){    var page = 0;    //由於第一頁沒有pushState,所以返回到第一頁的時候是沒有資料的,因此得做下判斷    if(event.state !== null){      page = event.state.page;    }    makeRequest(page);     pageIndex = page;  });

     state資料通過event傳進來,這樣就可以得到pageIndex。


但是,這樣實現還有問題,在第二頁的時候如果重新整理頁面的話,會發生錯亂,如下所示:首先點下一頁到第二頁,然後重新整理頁面,出現第一頁,再點下一頁,出現第二頁,點返回時出現問題,顯示還是第二頁,不是期望的第一頁,直到再次點返回時才是第一頁:

 從右邊的工具列可以發現,點第一次返回的時候擷取到的pageIndex仍然是1。對於這種情況,需要分析history模型,如下所示:

 可以理解為對history的操作,瀏覽器有一個隊列,用來存放訪問的記錄,包括每個訪問的網址還有state資料。一開始,隊列的首指標指向page = 0的位置,點下一頁時,執行了pushState,在這個隊列插入了一個元素,同時通過pushState操作記錄了這個元素的url和state資料。 在這裡可以看出,pushState的操作最重要的作用還是給history隊列插入元素,這樣瀏覽器的後退按鈕才不是置灰的狀態,其次才是上面說的存放 資料。點後退的時候,隊首指標後退一步指向page = 0的位置,點前進時又前進指向page = 1的位置。

如果在page = 1的位置重新整理頁面,模型是這個樣子的:

在第2步重新整理的時候,頁面的pageIndex又恢複成預設值0,所以page = 0,顯示第一頁資料,但是history所用的隊列並沒有改變。然後再點下一頁時,又給這個隊列push了一個元素,這個隊列就有兩個pageIndex 為1的元素,所以必須得兩次返回才能回到page = 0的位置,也就是上面說的錯亂的情況。

根據上面的分析,這樣的實現是有問題的,一但使用者不是在page = 0的位置重新整理頁面,就會出現需要點多次返回按鈕才能夠回到原先的頁面。

所以得在重新整理的時候,把當前頁的state資料更新一下,用replaceState,替換隊列隊首指標的資料,也就是當前頁的資料。方法是頁面初始化時replace一下:

window.history.replaceState({page: pageIndex /*此處為0*/}, null, window.location.href);
這樣模型就變成:

但其實使用者重新整理的時候更希望的是還是顯示當前頁,而不是回到第一頁。一個解決辦法是用當前頁的window.history.state資料,這個屬性瀏覽器支援得比較晚。在頁面初始化時設定pageIndex時就從history.state取:

 

var pageIndex = window.history.state === null ? 0 : window.history.state.page; 

      safari裡面的history.state是最近執行pushState傳入的資料,因此這個辦法在chrome/firefox裡面行得通,但是safari行不通。

      第二種辦法是藉助h5的localStorage存放當前頁數:

 //頁面初始化,取當前第幾頁先從localStorage取  var pageIndex = window.localStorage.pageIndex || 0;  function nextPage(){    //將頁面的index加1,同時存放在localStorage    window.localStorage.pageIndex = ++pageIndex;    //重新發請求和頁面載入    makeRequest(pageIndex);    window.history.pushState({page: pageIndex}, null, window.location.href);   }  window.addEventListener("popstate", function(event){    var page = 0;    if(event.state !== null){      page = event.state.page;    }    makeRequest(page);     //點擊返回或前進時,需要將page放到localStorage    window.localStorage.pageIndex = page;  });

      將頁面中所有改變pageIndex的地方,同時放到localStorage。這樣重新整理頁面的時候就可以取到當前頁的pageIndex。

      上面的方法都是將pageIndex放到了state參數裡,還有一種方法是把它放到第三個參數url裡,也就是說通過改變當前頁網址的辦法。pageIndex從網址裡面取:

 //當前第幾頁   var pageIndex = window.location.search.replace("?page=", "") || ;   function nextPage(){     //將頁面的index加     ++pageIndex;     //重新發請求和頁面載入     makeRequest(pageIndex);     window.history.pushState(null, null, "?page=" + pageIndex);   }

      注意,一旦執行了第8行的pushState,當前網址的地址就會發生變化。

      有一點需要注意的是,window.history.length雖然返回是的當前隊列的元素個數,但不代表history本身就是那個隊列,通過不同瀏覽器的對history[i]的輸出:

   可以看到history是一個數組,它的作用是讓使用者拿到history.length,當前的長度,但是填充的內容是不確定的。 

      除了使用history之外,還有藉助hash的方法,網易新聞就是使用了這樣的方法:

   //當前第幾頁   var pageIndex = window.location.hash.replace("#page=", "") || ;   function nextPage(){      makeRequest(pageIndex);     window.location.hash = "#page=" + pageIndex;   }   window.addEventListener("hashchange", function(){     var page = window.location.hash.replace("#page=", "") || ;     makeRequest(page);   });

      關於支援性,參考caniuse網站:history IE10及以上支援,hashchange的支援性較好,IE8及以上都支援。

      雖然hashchange的支援性較好,但是history的優點是可以傳資料。對一些複雜的應用可能會有很大的發揮作用,同時history支援back/go操作。

以上本文關於h5的history改善ajax列表請求體驗,希望大家喜歡。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.