2012-01-20 84 views
4

因此,我使用HTML5歷史記錄管理功能來添加在帶有AJAX加載的子內容的網站中前後導航的功能。如何在HTML5歷史記錄狀態下存儲功能

現在我想將狀態對象中的javascript函數存儲到彈出狀態的回調函數中。或多或少像下面的代碼:

$(window).bind("popstate", function(event) { 
    var state = event.originalEvent.state; 
    if (!state) { 
     return; 
    } 
    state.callback(state.argument); 
} 

function beforeLoad() { 
    var resourceId = "xyz"; 
    var func; 

    if (case1) { 
     func = switchPageToMode1; 
    } else { // case 2 
     func = swithPageToMode2; 
    } 

    func(resourceId); // run the function 
    window.history.pushState({ callback: func, resourceId: resourceId }, "newTitle", "newURL"); // and push it to history 
} 

function switchPageToMode1(resourceId) { 
    alterPageLayoutSomeWay(); 
    loadResource(resourceId); 
} 

function swithPageToMode2(resourceId) { 
    alterPageLayoutSomeOtherWay(); 
    loadResource(resourceId); 
} 

function loadResource(resourceId) { 
    ... 
} 

好的。所以我想要做的是存儲一個JavaScript函數的引用。但是推的狀態(實際window.history.pushState調用)時,瀏覽器文件的投訴,即錯誤:「DATA_CLONE_ERR:DOM異常25」

任何人知道我在做什麼錯?是否可以在狀態中存儲函數調用?

回答

6

不,不可能,不能直接反正。 According to MDC「狀態對象」,即pushState的第一個參數,「可以是任何可以序列化的內容」。不幸的是,你不能序列化一個函數。 WHATWG spec說基本上是相同的東西,但用更多的話說,其中的要點是,在狀態對象中顯式禁止功能。

的解決辦法是將存儲是一個字符串就可以eval或狀態對象函數的名稱,例如:

$(window).bind("popstate", function(event) { 
    var state = event.originalEvent.state; 

    if (!state) { return; } 

    window[ state.callback ](state.argument); // <-- look here 
} 

function beforeLoad() { 
    var resourceId = "xyz", 
     func 
    ; 

    if (case1) { 
    func = "switchPageToMode1"; // <-- string, not function 
    } else { 
    // case 2 
    func = "swithPageToMode2"; 
    } 

    window[ func ](resourceId); // <-- same here 

    window.history.pushState(
    { callback : func, 
     argument : resourceId 
    }, 
    "newTitle", "newURL" 
); 
} 

當然這是假設switchPageToMode1-2是在全球範圍內(即window),這不是最佳實踐。如果不是,它們必須以全球範圍內的某種方式訪問​​,例如[window.]MyAppGlobal.switchPageToMode1,在這種情況下,您可以撥打MyAppGlobal[ func ](argument)

+0

非常感謝!這解釋了一切;在序列化部分錯過了。 – stafffan

+0

@Jordan我存儲一個對象,它看起來像'{html:$('#main')。html()}',但即使我使用IE 11,我仍然在IE中收到'Data Clone Error' /或添加一些額外的歷史API,如https://github.com/devote/HTML5-History-API/blob/master/history.js。我還能做什麼錯? –

1

我想出了一個稍微不同的解決方案。

我加了兩個變量窗口變量

window.history.uniqueStateId = 0; 
window.history.data = {}. 

每次我執行pushState的,我要做的就是按下一個唯一的ID爲第一個參數

var data = { /* non-serializable data */ }; 
window.history.pushState({stateId : uniqueStateId}, '', url); 
window.history.data[uniqueStateId] = data; 

在popstate事件,然後我只需從狀態對象中獲取id並從數據對象中查找它。

+1

這實際上是一個時髦,仍然簡單的解決方案。愛它! – stafffan

+2

-1:這不會存活頁面重新加載。如果您使用後退按鈕瀏覽頁面,則前進按鈕返回到該頁面,您將發現您的所有狀態都消失了。 – Gili

+0

也不會做其他答案。如果您需要它,只需混合一些本地存儲解決方案即可 – futbolpal

0

這裏是我做的:

  • 每個HTML頁面包含一個可以創造新的歷史記錄條目的一個或多個組件。
  • 每個組件實現三種方法:
    1. getId()它返回其唯一的DOM ID。
    2. getState()返回該組件的狀態:

      { 
          id: getId(), 
          state: componentSpecificState 
      } 
      
    3. setState(state),更新使用上述值的組件的狀態。
  • 在頁面加載,我要初始化的組件ID映射到組件,如下所示:

    this.idToComponent[this.loginForm.getId()] = this.loginForm; 
    this.idToComponent[this.signupForm.getId()] = this.signupForm; 
    
  • 組件保存創造新的歷史記錄條目之前,他們的狀態: history.replaceState(this.getState(), title, href);

  • popstate事件被解僱時,我調用:

    var component = this.idToComponent[history.state.id]; 
    component.setState(history.state); 
    

總結:不是序列化function()我們序列化組件id和火的setState()功能。這種方法存在頁面加載。