我有一个AJAX应用。用户单击一个按钮,页面的显示就会改变。他们单击后退按钮,希望进入原始状态,但转而在浏览器中转到上一页。
如何截取并重新分配后退按钮事件?我已经研究过RSH之类的库(我无法使用...),而且我听说使用井号标签在某种程度上有所帮助,但我无法理解。
我有一个AJAX应用。用户单击一个按钮,页面的显示就会改变。他们单击后退按钮,希望进入原始状态,但转而在浏览器中转到上一页。
如何截取并重新分配后退按钮事件?我已经研究过RSH之类的库(我无法使用...),而且我听说使用井号标签在某种程度上有所帮助,但我无法理解。
Answers:
啊,后退按钮。您可能想象“后退”会触发一个JavaScript事件,您可以像这样简单地将其取消:
document.onHistoryGo = function() { return false; }
不对 根本没有这样的事件。
如果您确实有一个Web应用程序(而不只是一个具有ajaxy功能的网站),那么接管后退按钮(如您提到的URL上的片段)是合理的。Gmail会这样做。我说的是什么时候您的Web应用程序在一个页面中完全独立。
该技术很简单-每当用户执行修改操作时,都将重定向到您已经使用的相同URL,但具有不同的哈希片段。例如
window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";
如果您的Web应用程序的整体状态是可哈希的,那么使用哈希的好地方。假设您有一封电子邮件列表,也许您将它们的所有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);
使用jQuery,我做了一个简单的解决方案:
$(window).on('hashchange', function() {
top.location = '#main';
// Eventually alert the user describing what happened
});
到目前为止,虽然仅在Google Chrome中进行了测试。
这解决了我的Web应用程序的问题,该应用程序也高度基于AJAX。
也许有点hack-ish,但我将其称为优雅的hacking ;-)每当您尝试向后导航时,它都会在URI中弹出一个哈希部分,从技术上讲,它就是试图向后导航的内容。
它拦截了单击浏览器按钮和鼠标按钮的行为。而且,您也不能通过每秒单击几次来向后暴力破解,这是在基于setTimeout或setInterval的解决方案中会出现的问题。
我非常感谢Darkporter回答中的解释,但是我认为可以通过使用“ hashchange ”事件来改善它。正如darkporter所解释的那样,您要确保所有按钮都更改window.location.hash值。
<button>
元素,然后将事件附加到元素上,然后再进行设置window.location.hash = "#!somehashstring";
。<a href="#!somehashstring">Button 1</a>
。单击这些链接后,哈希将自动更新。我在井号后加上一个感叹号的原因是为了满足Google的“ hashbang”范式(了解更多信息),如果您希望被搜索引擎索引,这将非常有用。您的哈希字符串通常是名称/值对之类的#!color=blue&shape=triangle
或列表之类的#!/blue/triangle
-对您的Web应用而言有意义。
然后,您只需要添加这段代码即可,只要哈希值发生变化(包括按下后退按钮),该代码就会触发。似乎没有轮询循环是必要的。
window.addEventListener("hashchange", function(){
console.log("Hash changed to", window.location.hash);
// .... Do your thing here...
});
我没有在Chrome 36上进行任何测试,但是根据caniuse.com的说法,它应该可以在IE8 +,FF21 +,Chrome21 +和除Opera Mini之外的大多数其他浏览器中使用。
禁用浏览器的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中看到它的工作以及更多示例
谢谢 !
我摸索出了一种覆盖正常的历史行为,创造独特back
和forward
按钮事件,使用HTML5历史API(它不会在IE 9运行)。这是非常棘手的,但是如果您想拦截后退和前进按钮事件并根据需要进行处理,则非常有效。在许多情况下这可能很有用,例如,如果您正在显示远程桌面窗口,并且需要在远程计算机上重现后退和前进按钮的单击,则可能会很有用。
以下内容将覆盖后退和前进按钮的行为:
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或状态2。 3,然后我们可以确定用户按下的方向。就像这样,我们拦截了一个后退或前进按钮事件。然后,我们根据需要处理它并返回状态2,以便我们可以捕获下一个历史记录导航事件。
显然,在使用HistoryButtonOverride脚本时,您需要避免使用history.replaceState和history.pushState方法,否则您将破坏它。
由于隐私问题,无法禁用后退按钮或检查用户的历史记录,但是可以在此历史记录中创建新条目而无需更改页面。每当您的AJAX应用程序更改状态时,请top.location
使用新的URI片段更新。
top.location = "#new-application-state";
这将在浏览器的历史记录堆栈中创建一个新条目。许多AJAX库已经可以处理所有无聊的细节,例如Really Simple History。
setTimeout()
darkporter的示例。
我做这样的事情。我与以前的应用程序状态保持数组。
发起为:
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();
在我的情况下,我想防止后退按钮将用户发送到最后一页,而是采取其他措施。通过使用该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
事件不会触发。根据您的情况,这也可能适合您。
浏览器中的“后退”按钮通常会尝试返回到先前的地址。浏览器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('#', ''))
}