所以我正在使用HTML5历史记录管理来添加在具有AJAX加载子内容的网站中前后导航的功能。
现在我想在状态对象中存储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”
有谁知道我做错了什么?是否可以在州内存储函数调用?
答案 0 :(得分:7)
不,这是不可能的,不是直接的。 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 )
。
答案 1 :(得分:1)
我提出了一个稍微不同的解决方案。
我在窗口变量
中添加了两个变量window.history.uniqueStateId = 0;
window.history.data = {}.
每次执行pushstate时,我所做的就是为第一个参数
推送一个唯一的idvar data = { /* non-serializable data */ };
window.history.pushState({stateId : uniqueStateId}, '', url);
window.history.data[uniqueStateId] = data;
在popstate事件中,我只是从状态对象中获取id并从数据对象中查找它。
答案 2 :(得分:0)
以下是我的工作:
getId()
返回其唯一的DOM ID。getState()
返回组件的状态:
{
id: getId(),
state: componentSpecificState
}
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()
函数。这种方法可以在页面加载后继续存在。