Javascript 如何检测浏览器后退按钮事件-跨浏览器

Javascript 如何检测浏览器后退按钮事件-跨浏览器,javascript,cross-browser,single-page-application,back,Javascript,Cross Browser,Single Page Application,Back,如何确定用户是否按下了浏览器中的“后退”按钮 如何在使用#URL系统的单页web应用程序中强制使用页内后退按钮 究竟为什么浏览器后退按钮不触发自己的事件 (注意:根据Sharky的反馈,我已经包含了检测退格的代码) 所以,我经常在网上看到这些问题,最近我自己也遇到了控制后退按钮功能的问题。在为我的应用程序(带有哈希导航的单页)搜索了几天最佳解决方案之后,我想出了一个简单、跨浏览器、无库的系统来检测后退按钮 大多数人建议使用: window.onhashchange = function() {

如何确定用户是否按下了浏览器中的“后退”按钮

如何在使用
#URL
系统的单页web应用程序中强制使用页内后退按钮

究竟为什么浏览器后退按钮不触发自己的事件

(注意:根据Sharky的反馈,我已经包含了检测退格的代码)

所以,我经常在网上看到这些问题,最近我自己也遇到了控制后退按钮功能的问题。在为我的应用程序(带有哈希导航的单页)搜索了几天最佳解决方案之后,我想出了一个简单、跨浏览器、无库的系统来检测后退按钮

大多数人建议使用:

window.onhashchange = function() {
 //blah blah blah
}
但是,当用户使用更改位置哈希的on-in-page元素时,也将调用此函数。当您的用户单击页面并向后或向前移动时,这不是最佳的用户体验

为了让您大致了解我的系统,当用户在界面中移动时,我将用前面的散列填充一个数组。它看起来像这样:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}
function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}
非常直截了当。我这样做是为了确保跨浏览器支持,以及对旧浏览器的支持。只需将新的散列传递给函数,它就会为您存储它,然后更改散列(然后将其放入浏览器的历史记录中)

我还使用了一个内页后退按钮,使用
lasthash
数组在页面之间移动用户。看起来是这样的:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}
function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}
因此,这将把用户移回最后一个散列,并从数组中删除最后一个散列(我现在没有前进按钮)

所以。如何检测用户是否使用了我的“返回页面”按钮或“浏览器”按钮

起初,我查看了
window.onbeforeunload
,但没有结果-只有当用户要更改页面时才会调用该函数。在使用哈希导航的单页应用程序中不会发生这种情况

因此,在进一步挖掘之后,我看到了尝试设置标志变量的建议。在我的例子中,这个问题是我会尝试设置它,但由于所有内容都是异步的,所以它不会总是在哈希更改中为if语句及时设置
.onMouseDown
并不总是在click中调用,并且将其添加到onclick不会足够快地触发它

这是我开始研究
文档
窗口
之间的区别的时候。我的最终解决方案是使用
document.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
。我使用此命令通过
窗口.onbeforeuload
事件询问用户是否要离开

因此,简言之,对于不一定使用回页按钮或数组来存储历史的用户:

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 on):


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中

您可以尝试使用事件处理程序,例如:

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
is属性等于历史状态对象

对于jQuery语法,将其包装(以便在文档准备就绪后添加侦听器):

另见:


另请参见第页的示例:


//jQuery
$(窗口).on('popstate',函数(e){
var state=e.originalEvent.state;
如果(状态!==null){
//使用ajax加载内容
}
});
//香草javascript
window.addEventListener('popstate',函数(e){
var状态=e状态;
如果(状态!==null){
//使用ajax加载内容
}
});

这应该与Chrome 5+、Firefox 4+、IE 10+、Safari 6+、Opera 11.5+等兼容。

我尝试了上述选项,但没有一个对我有效。这是解决办法

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...");
        }
    }
if(window.event)
{
if(window.event.clientX<40&&window.event.clientY<0)
{
警报(“单击浏览器后退按钮…”);
}
其他的
{
警报(“单击浏览器刷新按钮…”);
}
}
有关更多详细信息,请参阅此链接浏览器:

对于移动控制:

$(文档).ready(函数(){
$('body')。在('click touch','#share',函数(e)上{
$('.share').fadeIn();
});
});
//杰里·布托努努·亚克拉马
window.onhashchange=函数(e){
var oldURL=e.oldURL.split(“#”)[1];
var newURL=e.newURL.split(“#”)[1];
如果(oldURL==“共享”){
$('.share').fadeOut();
E
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...");
        }
    }
(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);
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
  }
}
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

        }
    });
if(performance.navigation.type == 2)
{
    //Do your code here
}
history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };
$(window).on('popstate', function(event) {
 alert("pop");
});
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();
}
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}
var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
    console.log(perfEntries[i].type);
}
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' )