JS - window.history - 删除状态
JS - window.history - Delete a state
使用 html5 window.history
API,我可以很好地控制我的网络应用程序上的导航。
该应用目前有两种状态:selectDate
(1) 和 enterDetails
(2)。
应用程序加载时,我 replaceState
并设置 popState
侦听器:
history.replaceState({stage:"selectDate",...},...);
window.onpopstate = function(event) {
that.toStage(event.state.stage);
};
选择日期并且应用程序移至阶段2 i将状态2推到堆栈上:
history.pushState({stage:"enterDetails",...},...);
只要详细信息发生变化,此状态就会被替换,因此它们会保存在历史记录中。
可以通过三种方式离开第 2 阶段:
- 保存(ajax提交)
- 取消
- 后退按钮
后退按钮由 popstate
侦听器处理。取消按钮 按下 阶段 1,以便用户可以返回到他们输入后退按钮的详细信息。这些都很好用。
保存按钮应恢复到阶段 1 并且不允许用户导航回详细信息页面(因为他们已经提交了)。基本的,它应该使历史堆栈长度为 1。
但是好像没有history.delete()
,或者history.merge()
。我能做的最好的是 history.replaceState(stage1)
它将历史堆栈保留为:["selectDate","selectDate"]
.
如何去掉一层?
编辑:
想到别的,也没用
history.back(); //moves history to the correct position
location.href = "#foo"; // successfully removes ability to go 'forward',
// but also adds another layer to the history stack
这使得历史堆栈成为 ["selectDate","selectDate#foo"]
。
那么,作为替代方案,有没有办法在不推送新状态的情况下删除 'forward' 历史记录?
您现在可能已经继续前进,但是...据我所知,没有办法删除历史条目(或状态)。
我一直在研究的一个选择是在 JavaScript 中自己处理历史记录,并使用 window.history
对象作为各种载体。
基本上,当页面首次加载时,您创建自定义历史记录对象(我们将在此处使用数组,但使用对您的情况有意义的任何内容),然后执行初始 pushState
。我会将您的自定义历史记录对象作为状态对象传递,因为如果您还需要处理用户离开您的应用程序并稍后返回的情况,它可能会派上用场。
var myHistory = [];
function pageLoad() {
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data.
}
现在,当您导航时,您可以添加到您自己的历史对象(或者不添加 - 历史现在掌握在您手中!)并使用 replaceState
让浏览器远离循环。
function nav_to_details() {
myHistory.push("page_im_on_now");
window.history.replaceState(myHistory, "<name>", "<url>");
//Load page data.
}
当用户向后导航时,他们会触发您的 "base" 状态(您的状态对象将为空),您可以根据自定义历史对象处理导航。之后,您执行另一个 pushState。
function on_popState() {
// Note that some browsers fire popState on initial load,
// so you should check your state object and handle things accordingly.
// (I did not do that in these examples!)
if (myHistory.length > 0) {
var pg = myHistory.pop();
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data for "pg".
} else {
//No "history" - let them exit or keep them in the app.
}
}
用户将永远无法使用他们的浏览器按钮向前导航,因为他们总是在最新的页面上。
从浏览器的角度来看,每次他们去 "back",他们立即又向前推进。
从用户的角度来看,他们可以在页面中向后导航但不能向前导航(基本上模拟智能手机 "page stack" 型号)。
从开发人员的角度来看,您现在可以高度控制用户如何在您的应用程序中导航,同时仍然允许他们使用浏览器上熟悉的导航按钮。您可以根据需要从历史链中的任何位置 add/remove 项。如果您在历史数组中使用对象,您还可以跟踪有关页面的额外信息(如字段内容等)。
如果您需要处理用户启动的导航(例如用户在基于散列的导航方案中更改 URL),那么您可能会使用稍微不同的方法,例如...
var myHistory = [];
function pageLoad() {
// When the user first hits your page...
// Check the state to see what's going on.
if (window.history.state === null) {
// If the state is null, this is a NEW navigation,
// the user has navigated to your page directly (not using back/forward).
// First we establish a "back" page to catch backward navigation.
window.history.replaceState(
{ isBackPage: true },
"<back>",
"<back>"
);
// Then push an "app" page on top of that - this is where the user will sit.
// (As browsers vary, it might be safer to put this in a short setTimeout).
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
// We also need to start our history tracking.
myHistory.push("<whatever>");
return;
}
// If the state is NOT null, then the user is returning to our app via history navigation.
// (Load up the page based on the last entry of myHistory here)
if (window.history.state.isBackPage) {
// If the user came into our app via the back page,
// you can either push them forward one more step or just use pushState as above.
window.history.go(1);
// or window.history.pushState({ isBackPage: false }, "<name>", "<url>");
}
setTimeout(function() {
// Add our popstate event listener - doing it here should remove
// the issue of dealing with the browser firing it on initial page load.
window.addEventListener("popstate", on_popstate);
}, 100);
}
function on_popstate(e) {
if (e.state === null) {
// If there's no state at all, then the user must have navigated to a new hash.
// <Look at what they've done, maybe by reading the hash from the URL>
// <Change/load the new page and push it onto the myHistory stack>
// <Alternatively, ignore their navigation attempt by NOT loading anything new or adding to myHistory>
// Undo what they've done (as far as navigation) by kicking them backwards to the "app" page
window.history.go(-1);
// Optionally, you can throw another replaceState in here, e.g. if you want to change the visible URL.
// This would also prevent them from using the "forward" button to return to the new hash.
window.history.replaceState(
{ isBackPage: false },
"<new name>",
"<new url>"
);
} else {
if (e.state.isBackPage) {
// If there is state and it's the 'back' page...
if (myHistory.length > 0) {
// Pull/load the page from our custom history...
var pg = myHistory.pop();
// <load/render/whatever>
// And push them to our "app" page again
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
} else {
// No more history - let them exit or keep them in the app.
}
}
// Implied 'else' here - if there is state and it's NOT the 'back' page
// then we can ignore it since we're already on the page we want.
// (This is the case when we push the user back with window.history.go(-1) above)
}
}
无法删除或读取过去的历史记录。
您可以尝试通过在自己的记忆中模拟历史并在每次发出 window popstate
事件时调用 history.pushState
来绕过它(由 提出) ,但它有很多缺点,会导致比在动态 Web 应用程序中完全不支持浏览器历史记录更糟糕的用户体验,因为:
- popstate 事件可能会在用户回到过去 ~2-3 个状态时发生
- popstate 事件可以在用户前进时发生
因此,即使您尝试通过构建虚拟历史来绕过它,它也很可能会导致您拥有空白历史状态的情况(进入 back/forward 什么都不做),或者在哪里back/forward 会完全跳过您的一些历史状态。
一个简单的解决方案:
var ismobilescreen = $(window).width() < 480;
var backhistory_pushed = false;
$('.editbtn').click( function()
{
// push to browser history, so back button will close the editor
// and not navigate away from site
if (ismobilescreen && !backhistory_pushed)
{
window.history.pushState('forward', null, window.location);
backhistory_pushed = true;
}
}
然后:
if (window.history && window.history.pushState)
{
$(window).on('popstate', function()
{
if (ismobilescreen && backhistory_pushed && $('.editor').is(':visible'))
{
// hide editor window (we initiate a click on the cancel button)
$('.editor:visible .cancelbtn').click();
backhistory_pushed = false;
}
});
}
结果:
- 用户打开编辑器DIV,历史状态被保存。
- 用户点击后退按钮,历史状态被考虑在内。
- 用户停留在页面上!
- 编辑器 DIV 已关闭,而不是返回导航。
一个问题:如果您在 DIV 上使用“取消”按钮并且这会隐藏编辑器,则用户必须点击手机的后退按钮两次才能返回到上一个 URL.
要解决此问题,您可以调用window.history.back();
自行删除历史条目,实际上删除状态。
例如:
$('.btn-cancel').click( function()
{
if (ismobilescreen && backhistory_pushed)
{
window.history.back();
}
}
或者,您可以将 URL 推入包含锚点的历史记录中,例如#editor
是否在最近URL 是否存在anchor 是否推送历史
使用 html5 window.history
API,我可以很好地控制我的网络应用程序上的导航。
该应用目前有两种状态:selectDate
(1) 和 enterDetails
(2)。
应用程序加载时,我 replaceState
并设置 popState
侦听器:
history.replaceState({stage:"selectDate",...},...);
window.onpopstate = function(event) {
that.toStage(event.state.stage);
};
选择日期并且应用程序移至阶段2 i将状态2推到堆栈上:
history.pushState({stage:"enterDetails",...},...);
只要详细信息发生变化,此状态就会被替换,因此它们会保存在历史记录中。
可以通过三种方式离开第 2 阶段:
- 保存(ajax提交)
- 取消
- 后退按钮
后退按钮由 popstate
侦听器处理。取消按钮 按下 阶段 1,以便用户可以返回到他们输入后退按钮的详细信息。这些都很好用。
保存按钮应恢复到阶段 1 并且不允许用户导航回详细信息页面(因为他们已经提交了)。基本的,它应该使历史堆栈长度为 1。
但是好像没有history.delete()
,或者history.merge()
。我能做的最好的是 history.replaceState(stage1)
它将历史堆栈保留为:["selectDate","selectDate"]
.
如何去掉一层?
编辑:
想到别的,也没用
history.back(); //moves history to the correct position
location.href = "#foo"; // successfully removes ability to go 'forward',
// but also adds another layer to the history stack
这使得历史堆栈成为 ["selectDate","selectDate#foo"]
。
那么,作为替代方案,有没有办法在不推送新状态的情况下删除 'forward' 历史记录?
您现在可能已经继续前进,但是...据我所知,没有办法删除历史条目(或状态)。
我一直在研究的一个选择是在 JavaScript 中自己处理历史记录,并使用 window.history
对象作为各种载体。
基本上,当页面首次加载时,您创建自定义历史记录对象(我们将在此处使用数组,但使用对您的情况有意义的任何内容),然后执行初始 pushState
。我会将您的自定义历史记录对象作为状态对象传递,因为如果您还需要处理用户离开您的应用程序并稍后返回的情况,它可能会派上用场。
var myHistory = [];
function pageLoad() {
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data.
}
现在,当您导航时,您可以添加到您自己的历史对象(或者不添加 - 历史现在掌握在您手中!)并使用 replaceState
让浏览器远离循环。
function nav_to_details() {
myHistory.push("page_im_on_now");
window.history.replaceState(myHistory, "<name>", "<url>");
//Load page data.
}
当用户向后导航时,他们会触发您的 "base" 状态(您的状态对象将为空),您可以根据自定义历史对象处理导航。之后,您执行另一个 pushState。
function on_popState() {
// Note that some browsers fire popState on initial load,
// so you should check your state object and handle things accordingly.
// (I did not do that in these examples!)
if (myHistory.length > 0) {
var pg = myHistory.pop();
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data for "pg".
} else {
//No "history" - let them exit or keep them in the app.
}
}
用户将永远无法使用他们的浏览器按钮向前导航,因为他们总是在最新的页面上。
从浏览器的角度来看,每次他们去 "back",他们立即又向前推进。
从用户的角度来看,他们可以在页面中向后导航但不能向前导航(基本上模拟智能手机 "page stack" 型号)。
从开发人员的角度来看,您现在可以高度控制用户如何在您的应用程序中导航,同时仍然允许他们使用浏览器上熟悉的导航按钮。您可以根据需要从历史链中的任何位置 add/remove 项。如果您在历史数组中使用对象,您还可以跟踪有关页面的额外信息(如字段内容等)。
如果您需要处理用户启动的导航(例如用户在基于散列的导航方案中更改 URL),那么您可能会使用稍微不同的方法,例如...
var myHistory = [];
function pageLoad() {
// When the user first hits your page...
// Check the state to see what's going on.
if (window.history.state === null) {
// If the state is null, this is a NEW navigation,
// the user has navigated to your page directly (not using back/forward).
// First we establish a "back" page to catch backward navigation.
window.history.replaceState(
{ isBackPage: true },
"<back>",
"<back>"
);
// Then push an "app" page on top of that - this is where the user will sit.
// (As browsers vary, it might be safer to put this in a short setTimeout).
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
// We also need to start our history tracking.
myHistory.push("<whatever>");
return;
}
// If the state is NOT null, then the user is returning to our app via history navigation.
// (Load up the page based on the last entry of myHistory here)
if (window.history.state.isBackPage) {
// If the user came into our app via the back page,
// you can either push them forward one more step or just use pushState as above.
window.history.go(1);
// or window.history.pushState({ isBackPage: false }, "<name>", "<url>");
}
setTimeout(function() {
// Add our popstate event listener - doing it here should remove
// the issue of dealing with the browser firing it on initial page load.
window.addEventListener("popstate", on_popstate);
}, 100);
}
function on_popstate(e) {
if (e.state === null) {
// If there's no state at all, then the user must have navigated to a new hash.
// <Look at what they've done, maybe by reading the hash from the URL>
// <Change/load the new page and push it onto the myHistory stack>
// <Alternatively, ignore their navigation attempt by NOT loading anything new or adding to myHistory>
// Undo what they've done (as far as navigation) by kicking them backwards to the "app" page
window.history.go(-1);
// Optionally, you can throw another replaceState in here, e.g. if you want to change the visible URL.
// This would also prevent them from using the "forward" button to return to the new hash.
window.history.replaceState(
{ isBackPage: false },
"<new name>",
"<new url>"
);
} else {
if (e.state.isBackPage) {
// If there is state and it's the 'back' page...
if (myHistory.length > 0) {
// Pull/load the page from our custom history...
var pg = myHistory.pop();
// <load/render/whatever>
// And push them to our "app" page again
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
} else {
// No more history - let them exit or keep them in the app.
}
}
// Implied 'else' here - if there is state and it's NOT the 'back' page
// then we can ignore it since we're already on the page we want.
// (This is the case when we push the user back with window.history.go(-1) above)
}
}
无法删除或读取过去的历史记录。
您可以尝试通过在自己的记忆中模拟历史并在每次发出 window popstate
事件时调用 history.pushState
来绕过它(由
- popstate 事件可能会在用户回到过去 ~2-3 个状态时发生
- popstate 事件可以在用户前进时发生
因此,即使您尝试通过构建虚拟历史来绕过它,它也很可能会导致您拥有空白历史状态的情况(进入 back/forward 什么都不做),或者在哪里back/forward 会完全跳过您的一些历史状态。
一个简单的解决方案:
var ismobilescreen = $(window).width() < 480;
var backhistory_pushed = false;
$('.editbtn').click( function()
{
// push to browser history, so back button will close the editor
// and not navigate away from site
if (ismobilescreen && !backhistory_pushed)
{
window.history.pushState('forward', null, window.location);
backhistory_pushed = true;
}
}
然后:
if (window.history && window.history.pushState)
{
$(window).on('popstate', function()
{
if (ismobilescreen && backhistory_pushed && $('.editor').is(':visible'))
{
// hide editor window (we initiate a click on the cancel button)
$('.editor:visible .cancelbtn').click();
backhistory_pushed = false;
}
});
}
结果:
- 用户打开编辑器DIV,历史状态被保存。
- 用户点击后退按钮,历史状态被考虑在内。
- 用户停留在页面上!
- 编辑器 DIV 已关闭,而不是返回导航。
一个问题:如果您在 DIV 上使用“取消”按钮并且这会隐藏编辑器,则用户必须点击手机的后退按钮两次才能返回到上一个 URL.
要解决此问题,您可以调用window.history.back();
自行删除历史条目,实际上删除状态。
例如:
$('.btn-cancel').click( function()
{
if (ismobilescreen && backhistory_pushed)
{
window.history.back();
}
}
或者,您可以将 URL 推入包含锚点的历史记录中,例如#editor
是否在最近URL 是否存在anchor 是否推送历史