我有一个AJAX应用程序。用户单击按钮,页面的显示会发生变化。他们点击后退按钮,期望进入原始状态,但是,他们会在浏览器中转到上一页。
如何拦截并重新分配后退按钮事件?我已经查看了像RSH这样的库(我无法工作......),我听说使用哈希标签会有所帮助,但我无法理解它。
答案 0 :(得分:114)
document.onHistoryGo = function() { return false; }
不是这样。根本没有这样的事件。
如果你真的有一个网络应用(而不仅仅是一个带有一些ajaxy功能的网站),接管后退按钮是合理的(如你所提到的那样,URL上有片段) 。 Gmail就是这么做的。我在谈论你的网页应用程序在一个页面中的所有内容,都是独立的。
该技术很简单 - 只要用户采取修改内容的操作,就会重定向到您已经使用的相同URL,但使用不同的哈希片段。 E.g。
window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";
如果您的网络应用程序的整体状态是可清除的,那么这是一个使用哈希的好地方。假设你有一个电子邮件列表,也许你会连接所有的ID和读/未读状态,并使用MD5哈希作为你的片段标识符。
这种重定向(仅限哈希)不会尝试从服务器获取任何内容,但它会在浏览器的历史列表中插入一个插槽。因此,在上面的示例中,用户点击“返回”,他们现在在地址栏中显示 #deleted_something 。他们再次回击,他们仍然在你的页面上,但没有哈希。然后再回来,他们实际回到他们来自的任何地方。
现在是困难的部分,当用户回击时让你检测到JavaScript(这样你就可以恢复状态)。您只需观察窗口位置并查看其何时发生变化。随着民意调查。 (我知道,哎呀,民意调查。好吧,现在没有更好的跨浏览器了)。您将无法判断他们是前进还是后退,因此您必须使用您的哈希标识符获得创意。 (也许是与序列号连接的哈希...)
这是代码的要点。 (这也是jQuery History插件的工作原理。)
var hash = window.location.hash;
setInterval(function(){
if (window.location.hash != hash) {
hash = window.location.hash;
alert("User went back or forward to application state represented by " + hash);
}
}, 100);
答案 1 :(得分:48)
提供旧的(但受欢迎的)问题的最新答案:
HTML5引入了
history.pushState()
和history.replaceState()
方法,分别允许您添加和修改历史记录条目。这些方法与window.onpopstate
事件一起使用。使用
history.pushState()
更改在更改状态后创建的XMLHttpRequest
个对象的HTTP标头中使用的引用者。引用者将是创建this
对象时窗口为XMLHttpRequest
的文档的URL。
来自:Mozilla开发者网络的Manipulating the browser history。
答案 2 :(得分:10)
使用jQuery我做了一个简单的解决方案:
$(window).on('hashchange', function() {
top.location = '#main';
// Eventually alert the user describing what happened
});
到目前为止,仅在Google Chrome中测试过。
这解决了我的网络应用程序的问题,该应用程序也是基于AJAX的。
这可能是一个小黑客 - 但我称之为优雅的黑客;-)每当你尝试向后导航时,它会在URI中弹出一个哈希部分,从技术上来说它会尝试向后导航。< / p>
它拦截了单击浏览器按钮和鼠标按钮。而且你不能通过每秒点击几次来向后强制它,这是基于setTimeout或setInterval的解决方案中可能出现的问题。
答案 3 :(得分:8)
我非常感谢darkporter's answer中给出的解释,但我认为可以使用"hashchange" event进行改进。正如darkporter所解释的那样,您希望确保所有按钮都更改window.location.hash值。
<button>
元素,然后将事件附加到它们,然后设置window.location.hash = "#!somehashstring";
。 <a href="#!somehashstring">Button 1</a>
。单击这些链接时,哈希会自动更新。我在哈希符号后面有一个感叹号的原因是为了满足谷歌的“hashbang”范例(read more about that),如果你想被搜索引擎索引,这很有用。您的哈希字符串通常是#!color=blue&shape=triangle
之类的名称/值对或#!/blue/triangle
之类的列表 - 无论对您的网络应用程序有什么意义。
然后你只需要添加这段代码,这些代码会在哈希值发生变化时触发(包括当后退按钮被击中时)。似乎没有必要进行轮询循环。
window.addEventListener("hashchange", function(){
console.log("Hash changed to", window.location.hash);
// .... Do your thing here...
});
除了Chrome 36之外,我还没有进行过任何测试,但according to caniuse.com,这应该适用于IE8 +,FF21 +,Chrome21 +以及Opera Mini以外的大多数其他浏览器。
答案 4 :(得分:5)
很容易禁用浏览器BACK按钮,就像下面的JQuery代码一样:
// History API
if( window.history && window.history.pushState ){
history.pushState( "nohb", null, "" );
$(window).on( "popstate", function(event){
if( !event.originalEvent.state ){
history.pushState( "nohb", null, "" );
return;
}
});
}
您可以在dotnsf和thecssninja
看到它的工作和更多示例谢谢!
答案 5 :(得分:4)
由于隐私问题,无法禁用后退按钮或检查用户的历史记录,但可以在此历史记录中创建新条目而无需更改页面。每当您的AJAX应用程序更改状态时,请使用新的URI fragment更新top.location
。
top.location = "#new-application-state";
这将在浏览器的历史堆栈中创建一个新条目。许多AJAX库已经处理了所有无聊的细节,例如Really Simple History。
答案 6 :(得分:2)
在我的情况下,我想阻止后退按钮将用户发送到最后一页,而是采取不同的操作。使用hashchange
事件,我提出了一个对我有用的解决方案。这个脚本假定你使用它的页面还没有使用哈希,就像我的情况一样:
var hashChangedCount = -2;
$(window).on("hashchange", function () {
hashChangedCount++;
if (top.location.hash == "#main" && hashChangedCount > 0) {
console.log("Ah ah ah! You didn't say the magic word!");
}
top.location.hash = '#main';
});
top.location.hash = "#";
我遇到的其他一些问题的问题是hashchange
事件不会触发。根据您的具体情况,这也适用于您。
答案 7 :(得分:2)
我找到了一种方法来覆盖正常的历史记录行为,并使用HTML5历史记录API创建了不同的back
和forward
按钮事件(它在IE 9中无法工作)。这非常hacky,但如果你想拦截后退和前进按钮事件并按照你想要的方式处理它们,那就很有效。这在许多场景中都很有用,例如:如果您正在显示远程桌面窗口,并且需要在远程计算机上重现后退和前进按钮单击。
以下内容将覆盖后退和前进按钮行为:
var myHistoryOverride = new HistoryButtonOverride(function()
{
console.log("Back Button Pressed");
return true;
},
function()
{
console.log("Forward Button Pressed");
return true;
});
如果您在其中任何一个回调函数中返回false,您将允许浏览器继续正常的后退/前进操作并离开您的页面。
以下是完整的脚本:
function HistoryButtonOverride(BackButtonPressed, ForwardButtonPressed)
{
var Reset = function ()
{
if (history.state == null)
return;
if (history.state.customHistoryStage == 1)
history.forward();
else if (history.state.customHistoryStage == 3)
history.back();
}
var BuildURLWithHash = function ()
{
// The URLs of our 3 history states must have hash strings in them so that back and forward events never cause a page reload.
return location.origin + location.pathname + location.search + (location.hash && location.hash.length > 1 ? location.hash : "#");
}
if (history.state == null)
{
// This is the first page load. Inject new history states to help identify back/forward button presses.
var initialHistoryLength = history.length;
history.replaceState({ customHistoryStage: 1, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
history.pushState({ customHistoryStage: 2, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
history.pushState({ customHistoryStage: 3, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
history.back();
}
else if (history.state.customHistoryStage == 1)
history.forward();
else if (history.state.customHistoryStage == 3)
history.back();
$(window).bind("popstate", function ()
{
// Called when history navigation occurs.
if (history.state == null)
return;
if (history.state.customHistoryStage == 1)
{
if (typeof BackButtonPressed == "function" && BackButtonPressed())
{
Reset();
return;
}
if (history.state.initialHistoryLength > 1)
history.back(); // There is back-history to go to.
else
history.forward(); // No back-history to go to, so undo the back operation.
}
else if (history.state.customHistoryStage == 3)
{
if (typeof ForwardButtonPressed == "function" && ForwardButtonPressed())
{
Reset();
return;
}
if (history.length > history.state.initialHistoryLength + 2)
history.forward(); // There is forward-history to go to.
else
history.back(); // No forward-history to go to, so undo the forward operation.
}
});
};
这是一个简单的概念。当我们的页面加载时,我们创建3个不同的历史状态(编号为1,2和3)并将浏览器导航到状态编号2.因为状态2位于中间,下一个历史导航事件将使我们处于状态1或3,从中我们可以确定用户按下哪个方向。就这样,我们截获了一个后退或前进按钮事件。然后我们处理它然后我们想要并返回状态2,这样我们就可以捕获下一个历史导航事件。
显然,在使用HistoryButtonOverride脚本时,您需要避免使用history.replaceState和history.pushState方法,否则您就会破坏它。
答案 8 :(得分:1)
浏览器中的“后退”按钮通常会尝试返回到先前的地址。 浏览器javascript中没有本地设备后退按钮按下事件,但是您可以通过播放位置和哈希值来改变它的应用状态。
通过两个视图想象这个简单的应用程序:
要实现它,请遵循以下示例代码:
function goToView (viewName) {
// before you want to change the view, you have to change window.location.hash.
// it allows you to go back to the previous view with the back button.
// so use this function to change your view instead of directly do the job.
// page parameter is your key to understand what view must be load after this.
window.location.hash = page
}
function loadView (viewName) {
// change dom here based on viewName, for example:
switch (viewName) {
case 'index':
document.getElementById('result').style.display = 'none'
document.getElementById('index').style.display = 'block'
break
case 'result':
document.getElementById('index').style.display = 'none'
document.getElementById('result').style.display = 'block'
break
default:
document.write('404')
}
}
window.addEventListener('hashchange', (event) => {
// oh, the hash is changed! it means we should do our job.
const viewName = window.location.hash.replace('#', '') || 'index'
// load that view
loadView(viewName)
})
// load requested view at start.
if (!window.location.hash) {
// go to default view if there is no request.
goToView('index')
} else {
// load requested view.
loadView(window.location.hash.replace('#', ''))
}
答案 9 :(得分:0)
我这样做。我保留了一个以前应用程序状态的数组。
发起人:
var backstack = [];
然后我会听取位置哈希的变化,当它改变时我会这样做:
if (backstack.length > 1 && backstack[backstack.length - 2] == newHash) {
// Back button was probably pressed
backstack.pop();
} else
backstack.push(newHash);
通过这种方式,我可以通过一种简单的方式跟踪用户历史记录。现在如果我想在应用程序中实现一个后退按钮(而不是浏览器 - 按钮),我就是这么做的:
window.location.hash = backstack.pop();