[英]How to Detect Browser Back Button event - Cross Browser
您如何確定地檢測用戶是否按下了瀏覽器中的后退按鈕?
如何使用#URL
系統在單頁 web 應用程序中強制使用頁內后退按鈕?
到底為什么瀏覽器后退按鈕不觸發自己的事件??
(注意:根據 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
}
}
你有它。 一種簡單的三部分方法來檢測后退按鈕的使用與關於哈希導航的頁面內元素。
編輯:
為確保用戶不使用退格鍵觸發 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();
}
}
});
});
您可以嘗試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);
另請參閱單頁應用程序和 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+ 和類似版本兼容。
我已經為這個要求苦苦掙扎了很長一段時間,並采用了上面的一些解決方案來實現它。 然而,我偶然發現了一個觀察結果,它似乎適用於 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
}
}
如果這有幫助,請告訴我。 如果遺漏了什么,我會很樂意理解。
在 javascript 中,導航類型2
表示點擊瀏覽器的后退或前進按鈕,瀏覽器實際上是從緩存中獲取內容。
if(performance.navigation.type == 2)
{
//Do your code here
}
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
alert('hello world');
}
這是唯一對我有用的解決方案(它不是單頁網站)。 它適用於 Chrome、Firefox 和 Safari。
正確答案已經存在以回答問題。 我想提及新的 JavaScript API PerformanceNavigationTiming ,它正在取代已棄用的performance.navigation 。
如果用戶使用后退或前進按鈕登陸您的頁面,以下代碼將登錄控制台“back_forward”。 在您的項目中使用它之前,請查看兼容性表。
var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
console.log(perfEntries[i].type);
}
這肯定會起作用(用於檢測后退按鈕點擊)
$(window).on('popstate', function(event) {
alert("pop");
});
瀏覽器: 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>
看到這個:
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
它工作正常......
在頁面底部添加了此代碼。 引用此檢測瀏覽器按鈕
window.addEventListener("pageshow", function(e) {
var is_back = e.persisted || (typeof window.performance !=
"undefined" && window.performance.navigation.type === 2);
if (is_back) {
// do the action.
}
});
我用上的技巧完成了此任務。添加了事件偵聽器,當有人使用瀏覽器后退按鈕返回頁面時,該事件監聽器將被調用,並且在該事件中重新加載了頁面。 希望能有所幫助。
只有重新定義 API(更改對象“歷史”的方法)才能實現成熟的組件。我將分享剛剛編寫的類。 在 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();
```
我的變種:
const inFromBack = performance && performance.getEntriesByType( 'navigation' ).map( nav => nav.type ).includes( 'back_forward' )
這是我的看法。 假設是,當 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);
我能夠使用這個線程和其他線程中的一些答案來讓它在 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();
}
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。
我通過跟蹤觸發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)
}
<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
}
});
上面的代碼對我有用。 在移動瀏覽器上,當用戶點擊后退按鈕時,我們希望恢復他之前訪問的頁面狀態。
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
}
正在尋找此問題的解決方案,並根據此處的一些答案以及History.pushState()
和WindowEventHandlers.onpopstate
的 MDN Web 文檔頁面組合了一個簡單的框架測試 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>
如果您通過調用瀏覽您的應用程序,瀏覽器將發出popstate
事件
window.history.pushState({},'','/to')
如果您手動將地址輸入地址欄並單擊后退按鈕,則不會觸發popstate
事件。
如果您使用這個簡化的功能在您的應用程序中導航
const navigate = (to) => {
window.history.pushState({}, ",", to);
};
那么這將起作用
const handlePopstate = () => {
console.log("popped");
};
window.addEventListener("popstate", handlePopstate);
我嘗試了上述選項,但沒有一個對我有用。 這是解決方案
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.