我有一个 AJAX 应用程序。用户点击按钮后,页面的显示会更改。他们单击返回按钮,希望返回到原始状态,但实际上,他们回到了浏览器中的前一页。
如何拦截并重新指定返回按钮事件?我已经研究了像 RSH 这样的库(但我无法让它工作...),我听说使用哈希标签可以在某种程度上帮助,但我搞不懂。
我有一个 AJAX 应用程序。用户点击按钮后,页面的显示会更改。他们单击返回按钮,希望返回到原始状态,但实际上,他们回到了浏览器中的前一页。
如何拦截并重新指定返回按钮事件?我已经研究了像 RSH 这样的库(但我无法让它工作...),我听说使用哈希标签可以在某种程度上帮助,但我搞不懂。
// this does not work
document.onHistoryGo = function() { return false; }
不是这样的。实际上并没有这样的事件。
如果你确实拥有一个Web应用程序(而不仅仅是具有某些Ajax功能的网站),那么采取控制后退按钮(使用URL片段,如你所提到的)是合理的。 Gmail就是这样做的。我说的是当你的Web应用程序全部在一个页面中,全部是自包含的时候。
技术很简单 - 每当用户执行修改内容的操作时,重定向到与您已经在的相同的URL,但使用不同的哈希片段。例如:
window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";
如果你的 Web 应用的整个状态可以哈希化,那么这是使用哈希的好地方。假设你有一组电子邮件,你可以将它们的 ID 和已读/未读状态连接起来并取一个 MD5 哈希值,然后将其用作片段标识符。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);
为了对一个古老(但广受欢迎)的问题提供最新的答案:
HTML5引入了
history.pushState()
和history.replaceState()
方法,允许您分别添加和修改历史记录条目。这些方法与window.onpopstate
事件配合使用。使用
history.pushState()
会更改在您更改状态后创建的XMLHttpRequest
对象中使用的HTTP头的引用者。引用者将是文档的URL,该文档的窗口在创建XMLHttpRequest
对象时是this
。
来源:来自Mozilla开发者网络的操作浏览器历史记录。
使用jQuery,我已经做出了一个简单的解决方案:
$(window).on('hashchange', function() {
top.location = '#main';
// Eventually alert the user describing what happened
});
到目前为止,仅在Google Chrome中进行了测试。
这为我基于AJAX的Web应用程序解决了问题。
这可能有点“hackish” - 但我会称其为优雅的黑客技巧;-)每当您尝试向后导航时,它会在URI中弹出一个哈希部分,这在技术上是它尝试向后导航的内容。
它拦截了单击浏览器按钮和鼠标按钮。并且您也无法通过每秒点击多次来强制它向后移动,这是使用setTimeout或setInterval基于解决方案中会出现的问题。
禁用浏览器返回按钮非常容易,像下面的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;
}
});
}
我非常感谢darkporter的答案中所给出的解释,但我认为可以通过使用"hashchange"事件来进行改进。就像darkporter所解释的那样,你需要确保所有按钮都能更改window.location.hash的值。
<button>
元素,然后将事件附加到它们上面,然后设置window.location.hash = "#!somehashstring";
。<a href="#!somehashstring">Button 1</a>
。当单击这些链接时,哈希自动更新。我在哈希符号后面加上感叹号是为了满足Google的“哈希马铃薯”范例(阅读更多),如果您想被搜索引擎索引,这是有用的。您的哈希字符串通常将是名称/值对,例如#!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。
我找到一种方式可以覆盖正常的历史记录行为,并创建独立的后退
和前进
按钮事件,使用HTML5历史API(它不能在IE 9中使用)。这很hacky,但如果您想要拦截后退和前进按钮事件并以任何方式处理它们,则效果很好。这在许多场景下都非常有用,例如,如果您正在显示远程桌面窗口并需要在远程计算机上复制后退和前进按钮点击。
以下代码将覆盖后退和前进按钮行为:
var myHistoryOverride = new HistoryButtonOverride(function()
{
console.log("Back Button Pressed");
return true;
},
function()
{
console.log("Forward Button Pressed");
return true;
});
function addEvent(el, eventType, handler) {
if (el.addEventListener) { // DOM Level 2 browsers
el.addEventListener(eventType, handler, false);
} else if (el.attachEvent) { // IE <= 8
el.attachEvent('on' + eventType, handler);
} else { // ancient browsers
el['on' + eventType] = handler;
}
}
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();
addEvent(window,"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.
}
});
};
由于隐私问题,不可能禁用后退按钮或检查用户历史记录,但可以在不更改页面的情况下创建新条目。每当您的AJAX应用程序更改状态时,请使用新的URI片段更新top.location
。
top.location = "#new-application-state";
setTimeout()
。 - Matthew我做类似这样的事情。我使用一个包含先前应用程序状态的数组。
初始化为:
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
事件无法触发。根据您的情况,这种方法也可能适用于您。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('#', ''))
}
// 不起作用
,这样那些快速浏览答案的人就知道你并不是在提供解决方案。 - matt forsythe