您如何确定地检测用户是否在浏览器中按下了后退按钮?
如何使用#URL系统在单页web应用程序中强制使用页面内返回按钮?
为什么浏览器的后退按钮不触发它们自己的事件!?
您如何确定地检测用户是否在浏览器中按下了后退按钮?
如何使用#URL系统在单页web应用程序中强制使用页面内返回按钮?
为什么浏览器的后退按钮不触发它们自己的事件!?
当前回答
浏览器会发出popstate事件,如果你通过你的应用程序调用导航
window.history.pushState({},'','/to')
如果您手动在地址栏中输入地址并单击后退按钮,popstate事件将不会被触发。
如果你用这个简化的功能在应用中导航
const navigate = (to) => {
window.history.pushState({}, ",", to);
};
这样就可以了
const handlePopstate = () => {
console.log("popped");
};
window.addEventListener("popstate", handlePopstate);
其他回答
只有重新定义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();
```
这将肯定工作(用于检测返回按钮点击)
$(window).on('popstate', function(event) {
alert("pop");
});
看到这个:
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
它工作得很好……
(注:根据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();
}
所以这将移动用户回到最后的哈希,并从数组中删除最后的哈希(我现在没有前进按钮)。
所以。如何检测用户是否使用了页面内的后退按钮或浏览器按钮?
起初我看着窗户。Onbeforeunload,但是没有用——只有当用户要更改页面时才会调用它。这在使用散列导航的单页应用程序中不会发生。
因此,在深入研究之后,我看到了尝试设置标志变量的建议。在我的情况下,这个问题是,我会试着设置它,但由于一切都是异步的,它并不总是在哈希中的if语句更改时设置。onmousedown并不总是在点击中调用,并将其添加到onclick中不会足够快地触发它。
这时我开始研究文档和窗口之间的区别。我的最终解决方案是使用文档设置标志。Onmouseover,并使用document.onmouseleave禁用它。
发生的情况是,当用户的鼠标在文档区域内(读取:呈现的页面,但不包括浏览器框架),我的布尔值被设置为true。一旦鼠标离开文档区域,布尔值就会变为false。
这样,我就可以换窗口了。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。我使用它来询问用户是否想要使用窗口离开。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
}
}
结果出来了。关于哈希导航,一种简单的、由三部分组成的方法来检测后退按钮的使用情况与页面内元素的使用情况。
编辑:
为了确保用户不会使用backspace来触发back事件,你还可以包括以下内容(感谢@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();
}
}
});
});
我通过跟踪触发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)
}