如何确定用户是否在浏览器中按下了返回按钮?
如何使用#URL
系统在单页面Web应用程序中强制使用内部返回按钮?
为什么浏览器的后退按钮不会触发它们自己的事件!?
如何确定用户是否在浏览器中按下了返回按钮?
如何使用#URL
系统在单页面Web应用程序中强制使用内部返回按钮?
为什么浏览器的后退按钮不会触发它们自己的事件!?
我能够使用该线程和其他资源中的一些答案使其在IE和Chrome/Edge上正常工作。对我来说,history.pushState 在IE11中不被支持。
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();
}
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();
```
这是我的看法。假设当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);
$(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。
这是我的一个侧边菜单实现,可以使用后退按钮关闭。 适用于Chrome/Edge/Android/...
<html>
<head>
<style>
.sidebarMenu {
height: 25%;
position: fixed;
left: 0;
width: 25%;
margin-top: 20px;
transform: translateX(-100%);
transition: transform 250ms ease-in-out;
background-color: #aaa;
opacity: 1;
}
.openSidebarMenu {
transition: all 0.3s;
box-sizing: border-box;
display: none;
}
.openSidebarMenu:checked ~ #sidebarMenu {
transform: translateX(0);
}
</style>
</head>
<body>
<input type="checkbox" class="openSidebarMenu" id="openSidebarMenu" onclick="sidemenu()">
<label for="openSidebarMenu">
Menu (click me or back button to close)
</label>
<div id="sidebarMenu" class="sidebarMenu" onclick="sideBarClose()">
<ul class="sidebarMenuInner">
<li>Menu 1</li>
<li>Menu 2</li>
</ul>
</div>
<script>
function sidemenu() {
var selection = document.getElementById("openSidebarMenu");
if (selection.checked) {
window.history.pushState({page: 1}, "", "");
checked = true;
window.onpopstate = function(event) {
var selection = document.getElementById("openSidebarMenu");
if(event && selection.checked) {
selection.checked = false;
console.log("event 1");
} else {
console.log("event 2");
}
}
} else {
history.back();
}
}
function sideBarClose() {
var selection = document.getElementById("openSidebarMenu");
if (selection.checked) {
selection.checked = false;
history.back();
}
}
</script>
</body>
</html>
我通过跟踪触发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)
}
如果您通过调用浏览器在应用程序中导航,浏览器将会发出 popstate
事件。
window.history.pushState({},'','/to')
如果您手动输入地址到地址栏并单击返回按钮,则不会触发popstate
事件。
如果您使用此简化函数在应用程序中导航
const navigate = (to) => {
window.history.pushState({}, ",", to);
};
那么这将会起作用
const handlePopstate = () => {
console.log("popped");
};
window.addEventListener("popstate", handlePopstate);
Kotlin/JS(React)的解决方案:
import org.w3c.dom.events.Event
import kotlin.browser.document
import kotlin.browser.window
...
override fun componentDidMount() {
window.history.pushState(null, document.title, window.location.href)
window.addEventListener("popstate", actionHandler)
}
...
val actionHandler: (Event?) -> Unit = {
window.history.pushState(
null,
document.title,
window.location.href
)
// add your actions here
}
我正在寻找解决这个问题的方法,并根据这里的一些答案和MDN Web Doc页面上的History.pushState()
和WindowEventHandlers.onpopstate
组合了一个简单的骨架测试HTML。
以下HTML和JavaScript很容易复制粘贴并进行测试。
可以与浏览器的后退和前进按钮、快捷键一起使用,添加到URL的更改(在某些情况下非常重要)。
足够简单,可以添加到现有代码的关键点,并且还可以扩展。
<html>
<body>
<div id="p1">Option 1</div>
<div id="p2">Option 2</div>
<div id="p3">Option 3</div>
<div id="p4">Option 4</div>
<div id="c"></div>
<script>
var chg={
set:function(str){
var d=document.getElementById("c");
d.textContent=str;
},
go:function(e){
var s={"p":this.id};
chg.set(s.p);
hstry.add(s);
}
};
var hstry={
add:function(s){
var u=new URL(window.location);
u.searchParams.set("x",s.p);
window.history.pushState(s,"",u);
},
adjust:function(state){
if(state.p){
chg.set(state.p);
}
}
};
window.onpopstate=function(e){
console.log("popstate, e.state:["+ JSON.stringify(e.state) +"]");
hstry.adjust(e.state);
}
window.onload=function(){
var i,d,a=["p1","p2","p3","p4"];
for(i=0;i<a.length;i++){
d=document.getElementById(a[i]);
d.addEventListener("click",chg.go,false);
}
}
</script>
</body>
</html>
<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
}
});
上述代码对我有效。在移动浏览器上,当用户点击返回按钮时,我们希望根据他之前的访问恢复页面状态。
ongoback
事件的提案:https://discourse.wicg.io/t/set-back-button-url-in-pwas/4112 - collimarco还没有答案
(九年后的hh)。 - undefined