2012-01-20 55 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()功能。这种方法存在页面加载。