在我們的項目中,有大量ajax查詢表單+結果列表的頁面,由於查詢結果是ajax返回的,當用戶點擊列表的某一項進入詳情頁之後,再點擊瀏覽器回退按鈕返回ajax查詢頁面,這時大家都知道查詢頁面的表單和結果都回到了預設狀態。 如果每次返回頁面都要重新輸入查詢條件,或有甚者還得轉到列表的第幾頁,那這種體驗用 ...
在我們的項目中,有大量ajax查詢表單+結果列表的頁面,由於查詢結果是ajax返回的,當用戶點擊列表的某一項進入詳情頁之後,再點擊瀏覽器回退按鈕返回ajax查詢頁面,這時大家都知道查詢頁面的表單和結果都回到了預設狀態。
如果每次返回頁面都要重新輸入查詢條件,或有甚者還得轉到列表的第幾頁,那這種體驗用戶真的要抓狂了。
在我們的項目中,寫了一個很簡單的JavaScript基類來處理location.hash從而保存頁面狀態,今天在此就分享給大家。
(本文的內容可能對於JavaScript初學者來講有點難度,因為涉及到JS面向對象的知識,如定義類、繼承、虛方法、反射等)
先看看我們的需求
我們的項目是一個基於微信的H5任務管理系統,要完成的頁面原型如下圖所示:
需求應該都很清晰,就是點擊查詢表單,用ajax返回查詢結果,然後點擊列表中的某一個任務進入任務詳情頁。由於管理員(項目經理)通常會一次處理多個任務,所以就會不斷在任務詳情頁跟查詢列表頁切換,這時如果按返回鍵不能保存查詢頁面狀態的話,那每次返回查詢頁面都要重新輸入查詢條件,這樣的體驗肯定是不能忍受的。
所以,我們需要想辦法將頁面狀態保存下來,以便用戶按回退鍵的時候,查詢條件和結果都還在。
解決思路
保存頁面狀態的思路有很多啦,但是我們覺得用location.hash應該是最好的方法。
思路如下:
- 用戶輸入查詢條件並點擊確定後,我們將查詢條件序列化成一個字元串,並通過“#”將查詢條件加到url後面得到一個新的url,然後調用location.replace(新的url)修改瀏覽器地址欄中的地址。
- 當用戶按回退鍵回退到查詢頁面時,也可以說是頁面載入時,將location.hash反序列化成查詢條件,然後將查詢條件更新到查詢表單並執行查詢即可。
思路很簡單,關鍵的地方就是location.replace方法,這個方法不僅僅是修改瀏覽器中地址欄的url,更重要的是會在window.history中替換當前頁面的記錄。如果不用location.replace方法,那麼每次回退都會回退到上一個查詢條件。當然,這樣的需求可能對某些項目還有用。
最終解決方案
如果本文只是分享上面的解決思路,那價值就不大了。本文的價值應該是我們寫的那個雖然簡單但是卻很強大的JavaScript類。
如果你看明白了上面的解決思路,那就看看這個簡單的JavaScript類吧:
1 (function() { 2 if (window.HashQuery) { 3 return; 4 } 5 window.HashQuery = function() { 6 7 }; 8 9 HashQuery.prototype = { 10 parseFromLocation: function() { 11 if (location.hash === '' || location.hash.length === 1) { 12 return; 13 } 14 var properties = location.hash.substr(1).split('|'); 15 var index = 0; 16 for (var p in this) { 17 if (!this.hasOwnProperty(p) || typeof this[p] != 'string') { 18 continue; 19 } 20 21 if (index < properties.length) { 22 this[p] = properties[index]; 23 if (this[p] === '-') { 24 this[p] = ''; 25 } 26 } 27 index++; 28 } 29 }, 30 updateLocation: function() { 31 var properties = []; 32 for (var p in this) { 33 if (!this.hasOwnProperty(p) || typeof this[p] != 'string') { 34 continue; 35 } 36 var value = this[p]; 37 properties.push(value === '' ? '-' : value); 38 } 39 var url = location.origin + location.pathname + location.search + "#" + properties.join('|'); 40 location.replace(url); 41 } 42 }; 43 })();
這個類只有2個方法,HashQuery.parseFromLocation() 方法從location.hash反序列化為HashQuery子類的實例,HashQuery.updateLocation() 方法將當前HashQuery子類的實例序列化並更新到window.location。
可以看到HashQuery這個類沒有任何屬性,那是因為我們只定義了一個基類,類的屬性都在子類中進行定義。這也是符合實際的,因為查詢條件都只有在具體的頁面才知道有哪些屬性。
另外,請註意這裡的序列化和反序列化。這裡的序列化僅僅是利用JavaScript反射機制將實例的所有字元串屬性(按順序)的值用“|”分隔;而序列化則是將字元串用“|”分隔後,再利用反射更新到實例的屬性(按順序)。
如何使用HashQuery類
使用的時候就非常簡單了。
第一步,定義一個子類,將需要用到的查詢條件都加到字元串屬性當中,如我們的代碼:
1 (function() { 2 window.TaskSearchHashQuery = function () { 3 HashQuery.constructor.call(this); 4 this.iterationId = ''; 5 this.assignedUserId = ''; 6 this.status = ''; 7 this.keyword = ''; 8 }; 9 10 TaskSearchHashQuery.constructor = TaskSearchHashQuery; 11 TaskSearchHashQuery.prototype = new HashQuery(); 12 })();
第二步,在查詢頁面調用HashQuery.parseFromLocation() 和 HashQuery.updateLocation()方法即可。下麵的代碼是我們完整的查詢頁面:
1 (function() { 2 var urls = { 3 list: "/app/task/list" 4 }; 5 var hashQuery = null; 6 var pager = null; 7 8 $(document).ready(function () { 9 hashQuery = new TaskSearchHashQuery(); 10 hashQuery.parseFromLocation();//在這裡調用的哦,從location反序列化object 11 updateFormByHashQuery(); 12 13 $("#btnSearch").click(function() { 14 updateHashQueryByForm(); 15 hashQuery.updateLocation();//在這裡調用的哦,將查詢條件序列化之後更新到location.hash 16 $("#lblCount").html("載入中..."); 17 pager.reload(); 18 page.hideSearch(); 19 }); 20 21 pager = new ListPager("#listTasks", urls.list); 22 pager.getPostData = function(index) { 23 return "pageIndex=" + index + "&pageSize=50" + "&projectId=" + page.projectId 24 + "&iterationId=" + hashQuery.iterationId 25 + "&assignedUserId=" + hashQuery.assignedUserId 26 + "&status=" + hashQuery.status 27 + "&keyword=" + hashQuery.keyword; 28 }; 29 pager.onLoaded = function() { 30 $("#lblCount").html("共 " + $("#hfPagerTotalCount").val() + " 個任務"); 31 $("#hfPagerTotalCount").remove(); 32 }; 33 pager.init(); 34 }); 35 36 function updateHashQueryByForm() { 37 hashQuery.iterationId = $("#ddlIterations").val(); 38 hashQuery.assignedUserId = $("#ddlUsers").val(); 39 hashQuery.status = $("#ddlStatuses").val(); 40 hashQuery.keyword = $("#txtKeyword").val(); 41 }; 42 43 function updateFormByHashQuery() { 44 $("#ddlIterations").val(hashQuery.iterationId); 45 $("#ddlUsers").val(hashQuery.assignedUserId); 46 $("#ddlStatuses").val(hashQuery.status); 47 $("#txtKeyword").val(hashQuery.keyword); 48 }; 49 50 })();
總結
這就是我們項目中使用location.hash來保存頁面狀態的全部知識了。不知道大家的WEB項目中是如何處理這樣的需求的呢?
最後,大家可以看看我們的開源項目:https://github.com/leotsai/mvcsolution,一個講ASP.NET MVC應用架構的項目,裡面也有不少JavaScript的架構和高級用法。