如何明确检测用户是否按下浏览器中的后退按钮?
如何使用#URL
系统在单页面Web应用程序中强制使用页内后退按钮?
为什么地球上没有浏览器按钮会触发自己的事件!?
答案 0 :(得分:142)
(注意:根据Sharky的反馈,我已经包含了检测退格的代码)
所以,我经常在SO上看到这些问题,并且最近遇到了自己控制后退按钮功能的问题。在为我的应用程序(带有散列导航的单页)搜索最佳解决方案几天后,我提出了一个简单的,跨浏览器,无库的系统来检测后退按钮。
大多数人建议使用:
window.onhashchange = function() {
//blah blah blah
}
但是,当用户使用更改位置哈希的页内元素时,也会调用此函数。当用户点击并且页面向后或向前移动时,这不是最好的用户体验。
为了给你一个我的系统的概述,当我的用户在界面中移动时,我用以前的哈希填充数组。它看起来像这样:
function updateHistory(curr) {
window.location.lasthash.push(window.location.hash);
window.location.hash = curr;
}
非常直接。我这样做是为了确保跨浏览器支持,以及对旧版浏览器的支持。只需将新哈希传递给函数,它就会为您存储它,然后更改哈希值(然后将其放入浏览器的历史记录中)。
我还利用页内返回按钮,使用lasthash
数组在页面之间移动用户。它看起来像这样:
function goBack() {
window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
//blah blah blah
window.location.lasthash.pop();
}
所以这会将用户移回到最后一个哈希,并从数组中删除最后一个哈希(我现在没有前进按钮)。
因此。如何检测用户是否使用了我的页内返回按钮或浏览器按钮?
起初我查看了window.onbeforeunload
,但无济于事 - 只有在用户要更改页面时才会调用。在使用哈希导航的单页面应用程序中不会发生这种情况。
因此,经过一些挖掘,我看到了尝试设置标志变量的建议。在我的情况下,这个问题是我会尝试设置它,但由于一切都是异步的,因此不会总是及时设置散列更改中的if语句。 .onMouseDown
始终未在点击中调用,并将其添加到onclick将不会足够快地触发它。
这时我开始研究document
和window
之间的区别。我的最终解决方案是使用document.onmouseover
设置标记,并使用document.onmouseleave
禁用它。
当用户的鼠标位于文档区域内时(读取:呈现的页面,但不包括浏览器框架),我的布尔值设置为true
。只要鼠标离开文档区域,布尔值就会翻转到false
。
这样,我可以将window.onhashchange
更改为:
window.onhashchange = function() {
if (window.innerDocClick) {
window.innerDocClick = false;
} else {
if (window.location.hash != '#undefined') {
goBack();
} else {
history.pushState("", document.title, window.location.pathname);
location.reload();
}
}
}
您需要注意#undefined
的支票。这是因为如果我的数组中没有可用的历史记录,则返回undefined
。我用它来询问用户是否想要使用window.onbeforeunload
事件离开。
因此,简而言之,对于那些不一定使用页内后退按钮或数组存储历史记录的人来说:
document.onmouseover = function() {
//User's mouse is inside the page.
window.innerDocClick = true;
}
document.onmouseleave = function() {
//User's mouse has left the page.
window.innerDocClick = false;
}
window.onhashchange = function() {
if (window.innerDocClick) {
//Your own in-page mechanism triggered the hash change
} else {
//Browser back button was clicked
}
}
你有它。一个简单的,由三部分组成的方法来检测后退按钮的使用情况与页面内元素相关的哈希导航。
编辑:
为了确保用户不使用退格键触发后退事件,您还可以包含以下内容(感谢this Question上的@thetoolman):
$(function(){
/*
* this swallows backspace keys on any non-input element.
* stops backspace -> back
*/
var rx = /INPUT|SELECT|TEXTAREA/i;
$(document).bind("keydown keypress", function(e){
if( e.which == 8 ){ // 8 == backspace
if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
e.preventDefault();
}
}
});
});
答案 1 :(得分:60)
您可以尝试popstate
事件处理程序,例如:
window.addEventListener('popstate', function(event) {
// The popstate event is fired each time when the current history entry changes.
var r = confirm("You pressed a Back button! Are you sure?!");
if (r == true) {
// Call Back button programmatically as per user confirmation.
history.back();
// Uncomment below line to redirect to the previous page instead.
// window.location = document.referrer // Note: IE11 is not supporting this.
} else {
// Stay on the current page.
history.pushState(null, null, window.location.pathname);
}
history.pushState(null, null, window.location.pathname);
}, false);
注意:为了获得最佳结果,您应该仅在要实现逻辑的特定页面上加载此代码,以避免任何其他意外问题。
每当当前历史记录条目更改(用户导航到新状态)时,就会触发popstate事件。当用户点击浏览器的后退/前进按钮或以编程方式调用history.back()
,history.forward()
,history.go()
方法时,就会发生这种情况。
event.state
是事件的属性,等于历史状态对象。
对于jQuery语法,将其包装起来(在文档准备好后添加偶数监听器):
(function($) {
// Above code here.
})(jQuery);
另请参阅:window.onpopstate on page load
另请参阅Single-Page Apps and HTML5 pushState页面上的示例:
<script>
// jQuery
$(window).on('popstate', function (e) {
var state = e.originalEvent.state;
if (state !== null) {
//load content with ajax
}
});
// Vanilla javascript
window.addEventListener('popstate', function (e) {
var state = e.state;
if (state !== null) {
//load content with ajax
}
});
</script>
这应与Chrome 5 +,Firefox 4 +,IE 10 +,Safari 6 +,Opera 11.5+及类似产品兼容。
答案 2 :(得分:13)
我一直在努力解决这个问题很长一段时间,并采取了上面的一些解决方案来实现它。但是,我偶然发现了一个观察结果,它似乎适用于Chrome,Firefox和Safari浏览器+ Android和iPhone
页面加载:
window.history.pushState({page: 1}, "", "");
window.onpopstate = function(event) {
// "event" object seems to contain value only when the back button is clicked
// and if the pop state event fires due to clicks on a button
// or a link it comes up as "undefined"
if(event){
// Code to handle back button or prevent from navigation
}
else{
// Continue user action through link or button
}
}
如果这有帮助,请告诉我。如果我遗失了什么,我将很乐意理解。
答案 3 :(得分:9)
在javascript中,导航类型2
表示单击了浏览器的后退或前进按钮,并且实际上是从缓存中获取内容。
if(performance.navigation.type == 2)
{
//Do your code here
}
答案 4 :(得分:4)
我的变体:
const inFromBack = performance && performance.getEntriesByType( 'navigation' ).map( nav => nav.type ).includes( 'back_forward' )
答案 5 :(得分:4)
这肯定会有效(用于检测单击后退按钮)
$(window).on('popstate', function(event) {
alert("pop");
});
答案 6 :(得分:4)
浏览器:https://jsfiddle.net/Limitlessisa/axt1Lqoz/
对于移动控件:https://jsfiddle.net/Limitlessisa/axt1Lqoz/show/
$(document).ready(function() {
$('body').on('click touch', '#share', function(e) {
$('.share').fadeIn();
});
});
// geri butonunu yakalama
window.onhashchange = function(e) {
var oldURL = e.oldURL.split('#')[1];
var newURL = e.newURL.split('#')[1];
if (oldURL == 'share') {
$('.share').fadeOut();
e.preventDefault();
return false;
}
//console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>
<head>
<title>Back Button Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body style="text-align:center; padding:0;">
<a href="#share" id="share">Share</a>
<div class="share" style="">
<h1>Test Page</h1>
<p> Back button press please for control.</p>
</div>
</body>
</html>
答案 7 :(得分:2)
这是我的看法。假设是,当URL发生变化但是检测到document
内没有点击时,它是浏览器返回(是,或前进)。用户单击将在2秒后重置,以便在通过Ajax加载内容的页面上执行此操作:
(function(window, $) {
var anyClick, consoleLog, debug, delay;
delay = function(sec, func) {
return setTimeout(func, sec * 1000);
};
debug = true;
anyClick = false;
consoleLog = function(type, message) {
if (debug) {
return console[type](message);
}
};
$(window.document).click(function() {
anyClick = true;
consoleLog("info", "clicked");
return delay(2, function() {
consoleLog("info", "reset click state");
return anyClick = false;
});
});
return window.addEventListener("popstate", function(e) {
if (anyClick !== true) {
consoleLog("info", "Back clicked");
return window.dataLayer.push({
event: 'analyticsEvent',
eventCategory: 'test',
eventAction: 'test'
});
}
});
})(window, jQuery);
答案 8 :(得分:2)
看到这个:
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
它工作正常...
答案 9 :(得分:2)
仅当您重新定义API(更改对象“ history”的方法)时,才能实现完整的组件 我将分享刚才写的课程。 在Chrome和Mozilla上测试 仅支持HTML5和ECMAScript5-6
class HistoryNavigation {
static init()
{
if(HistoryNavigation.is_init===true){
return;
}
HistoryNavigation.is_init=true;
let history_stack=[];
let n=0;
let current_state={timestamp:Date.now()+n};
n++;
let init_HNState;
if(history.state!==null){
current_state=history.state.HNState;
history_stack=history.state.HNState.history_stack;
init_HNState=history.state.HNState;
} else {
init_HNState={timestamp:current_state.timestamp,history_stack};
}
let listenerPushState=function(params){
params=Object.assign({state:null},params);
params.state=params.state!==null?Object.assign({},params.state):{};
let h_state={ timestamp:Date.now()+n};
n++;
let key = history_stack.indexOf(current_state.timestamp);
key=key+1;
history_stack.splice(key);
history_stack.push(h_state.timestamp);
h_state.history_stack=history_stack;
params.state.HNState=h_state;
current_state=h_state;
return params;
};
let listenerReplaceState=function(params){
params=Object.assign({state:null},params);
params.state=params.state!==null?Object.assign({},params.state):null;
let h_state=Object.assign({},current_state);
h_state.history_stack=history_stack;
params.state.HNState=h_state;
return params;
};
let desc=Object.getOwnPropertyDescriptors(History.prototype);
delete desc.constructor;
Object.defineProperties(History.prototype,{
replaceState:Object.assign({},desc.replaceState,{
value:function(state,title,url){
let params={state,title,url};
HistoryNavigation.dispatchEvent('history.state.replace',params);
params=Object.assign({state,title,url},params);
params=listenerReplaceState(params);
desc.replaceState.value.call(this,params.state,params.title,params.url);
}
}),
pushState:Object.assign({},desc.pushState,{
value:function(state,title,url){
let params={state,title,url};
HistoryNavigation.dispatchEvent('history.state.push',params);
params=Object.assign({state,title,url},params);
params=listenerPushState(params);
return desc.pushState.value.call(this, params.state, params.title, params.url);
}
})
});
HistoryNavigation.addEventListener('popstate',function(event){
let HNState;
if(event.state==null){
HNState=init_HNState;
} else {
HNState=event.state.HNState;
}
let key_prev=history_stack.indexOf(current_state.timestamp);
let key_state=history_stack.indexOf(HNState.timestamp);
let delta=key_state-key_prev;
let params={delta,event,state:Object.assign({},event.state)};
delete params.state.HNState;
HNState.history_stack=history_stack;
if(event.state!==null){
event.state.HNState=HNState;
}
current_state=HNState;
HistoryNavigation.dispatchEvent('history.go',params);
});
}
static addEventListener(...arg)
{
window.addEventListener(...arg);
}
static removeEventListener(...arg)
{
window.removeEventListener(...arg);
}
static dispatchEvent(event,params)
{
if(!(event instanceof Event)){
event=new Event(event,{cancelable:true});
}
event.params=params;
window.dispatchEvent(event);
};
}
HistoryNavigation.init();
// exemple
HistoryNavigation.addEventListener('popstate',function(event){
console.log('Will not start because they blocked the work');
});
HistoryNavigation.addEventListener('history.go',function(event){
event.params.event.stopImmediatePropagation();// blocked popstate listeners
console.log(event.params);
// back or forward - see event.params.delta
});
HistoryNavigation.addEventListener('history.state.push',function(event){
console.log(event);
});
HistoryNavigation.addEventListener('history.state.replace',function(event){
console.log(event);
});
history.pushState({h:'hello'},'','');
history.pushState({h:'hello2'},'','');
history.pushState({h:'hello3'},'','');
history.back();
```
答案 10 :(得分:1)
<input style="display:none" id="__pageLoaded" value=""/>
$(document).ready(function () {
if ($("#__pageLoaded").val() != 1) {
$("#__pageLoaded").val(1);
} else {
shared.isBackLoad = true;
$("#__pageLoaded").val(1);
// Call any function that handles your back event
}
});
上面的代码对我有用。在移动浏览器上,当用户单击“后退”按钮时,我们希望恢复其上次访问时的页面状态。
答案 11 :(得分:1)
我能够使用此线程中的某些答案,而其他答案则可使其在IE和Chrome / Edge中运行。 IE11不支持 history.pushState 。
if (history.pushState) {
//Chrome and modern browsers
history.pushState(null, document.title, location.href);
window.addEventListener('popstate', function (event) {
history.pushState(null, document.title, location.href);
});
}
else {
//IE
history.forward();
}
答案 12 :(得分:1)
document.mouseover不适用于IE和FireFox。 不过我试过这个:
$(document).ready(function () {
setInterval(function () {
var $sample = $("body");
if ($sample.is(":hover")) {
window.innerDocClick = true;
} else {
window.innerDocClick = false;
}
});
});
window.onhashchange = function () {
if (window.innerDocClick) {
//Your own in-page mechanism triggered the hash change
} else {
//Browser back or forward button was pressed
}
};
这适用于Chrome和IE,而不适用于FireFox。仍然努力使FireFox正确。检测浏览器后退/前进按钮点击的任何简单方法都是受欢迎的,特别是在JQuery中,还有AngularJS或普通Javascript。
答案 13 :(得分:0)
我通过跟踪触发hashchange
的原始事件(无论是滑动,点击还是滚轮)来解决它,以便事件不会被误认为是简单的着陆 - 在页面上,并在我的每个事件绑定中使用一个额外的标志。点击后退按钮时,浏览器不会再将标志设置为false
:
var evt = null,
canGoBackToThePast = true;
$('#next-slide').on('click touch', function(e) {
evt = e;
canGobackToThePast = false;
// your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}
答案 14 :(得分:0)
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
alert('hello world');
}
这是唯一对我有用的解决方案(它不是一页网站)。 它可以与Chrome,Firefox和Safari一起使用。
答案 15 :(得分:0)
正确的答案已经可以回答问题。我想提一下新的JavaScript API PerformanceNavigationTiming,它正在取代已弃用的performance.navigation。
如果用户使用“后退”或“前进”按钮登陆到您的页面,则以下代码将登录控制台“ back_forward”。 Take a look at compatibility table before using it in your project.
var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
console.log(perfEntries[i].type);
}
答案 16 :(得分:0)
我用下面的技巧完成了此任务。添加了事件监听器,当有人使用浏览器后退按钮返回页面时调用该事件监听器,该事件只是重新加载了页面,我只是检查了页面顺序并破坏了所有会话变量以完全注销用户。希望有帮助。
window.addEventListener("pageshow", function(event) {
var historyTraversal = event.persisted || (typeof window.performance !=
"undefined" && window.performance.navigation.type === 2);
if (historyTraversal) {
// Handle page restore.
window.location.reload();
}
});
在页面底部添加了此代码。
答案 17 :(得分:-16)
我尝试了上述选项,但没有一个适合我。这是解决方案
if(window.event)
{
if(window.event.clientX < 40 && window.event.clientY < 0)
{
alert("Browser back button is clicked...");
}
else
{
alert("Browser refresh button is clicked...");
}
}
有关详细信息,请参阅此链接http://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handli