如何检测浏览器的“后退”按钮事件 - 跨浏览器

367

如何确定用户是否在浏览器中按下了返回按钮?

如何使用#URL系统在单页面Web应用程序中强制使用内部返回按钮?

为什么浏览器的后退按钮不会触发它们自己的事件!?


4
希望能够添加导航事件(后退/前进)。到目前为止,这是一个正在进行中的工作。https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming - Howdy
请支持对于 ongoback 事件的提案:https://discourse.wicg.io/t/set-back-button-url-in-pwas/4112 - collimarco
3
这些按钮是否不允许拦截,因为这样会使应用程序在页面上“锁定”浏览器,这种情况是否可能发生? - William T. Mallard
1
已经有300个赞,但还没有一个有效的答案。 - greendino
@greendino - 这个问题与API本身有关(没有解释为什么popstate不存储方向-后退与前进)。简直糟糕透顶。这里的所有答案都检测到“两个”事件(后退和前进)。还没有答案(九年后的hh)。 - undefined
22个回答

248
(注:根据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中永远不会快速触发它。

这就是我开始查看documentwindow之间的区别的原因。我的最终解决方案是使用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
    }
}

就是这样。一种简单的、三部分的方法,用于检测后退按钮的使用与页面内元素相比,以实现哈希导航。

编辑:

为了确保用户不使用退格键触发返回事件,您还可以包括以下内容(感谢@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();
            }
        }
    });
});

10
+1 不错的想法,但我认为如果用户在鼠标位于浏览器窗口内时使用“返回”键(Firefox浏览器上的后退空格键),则此功能将失败。 - Sharky
4
好的 - 我现在就在办公室 :) (编辑:已完成) - Xarus
17
移动设备(例如iPad)怎么样? - basarat
8
MAC上的触控板滑动事件能否被捕获? - Sriganesh Navaneethakrishnan
6
我该如何区分前进和后退按钮? - Mr_Perfect
显示剩余10条评论

148
你可以尝试使用 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
Kenorb,你是正确的,popstate 可以用来获取变化,但它不能区分程序调用事件和用户点击浏览器按钮之间的差异。 - Xarus
1
关于“单页应用和HTML5 pushState”的文章非常棒,适用于现代的“单页布局”或“单页应用程序”。感谢您提供链接和答案! - lowtechsun
1
在现代浏览器中,e.state 似乎总是未定义的。 - FAjir
1
popstate事件只有在执行浏览器操作时才会被触发,例如单击后退按钮(或在JavaScript中调用history.back()),在两个相同文档的历史记录条目之间导航时。 - pref
2
“popstate”事件在点击“返回”按钮时不会被触发。 - Hermann Schwarz
显示剩余2条评论

32
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

这是唯一一个对我有效的解决方案(它不是单页网站)。 它在 Chrome、Firefox 和 Safari 中都可以正常工作。


8
window.performance.navigation已被弃用。这里是文档链接https://developer.mozilla.org/en-US/docs/Web/API/Performance/navigation - Howdy
这个方法在我的.cshtml页面上有效。在Chrome和IE上成功运行。谢谢。 - Justjyde
2
这段代码在弃用之后仍然有效,但我不确定它是否跨浏览器兼容:if (String(window.performance.getEntriesByType("navigation")[0].type) === "back_forward") { // 在此处编写你的代码 } - Mladen Adamovic
1
天啊!我试了很多变体,但只有你的解决方案有效!!!谢谢你!你在我工作了3个小时后为我省下了时间! - Hottabov
这是唯一可行的解决方案!!! popstate 事件(之前的解决方案)无法在按下“返回”按钮时触发。 - Hermann Schwarz

25

我曾经为这个要求苦苦挣扎了很长时间,尝试了一些上面提到的解决方案来实现它。然而,我偶然发现了一个观察结果,并且它似乎适用于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
  }
}

如果这有帮助,请告诉我。如果我漏掉了什么,我很乐意了解。


13
不正确。即使对于前进按钮,“event”的值仍然存在。 - Daniel Birowsky Popeski

23

这不是判断在此页面上是否点击了返回按钮的方法。它只能告诉我们上一页是否点击了返回按钮。 - Seph Reed
5
但碰巧这正是我在寻找的实际答案,因为我遇到的问题是在我点击后退按钮之后而不是之前,这意味着我可以编写一些响应代码来停止我的重复请求问题。谢谢。 - Andrew
3
这可能并不能回答问题,但正是我需要的!不错 - 我之前不知道这个... - Hogsmill

22

在JavaScript中,导航类型2表示浏览器的后退或前进按钮被点击,并且浏览器实际上是从缓存中获取内容。

if(performance.navigation.type == 2)
{
    //Do your code here
}

8
这个在单页应用程序上不起作用,结果总是1(表示重新加载)。此外,它不能区分“后退”和“前进”按钮,这非常不幸。 - papillon
3
这个现在已经被弃用了。请参见:https://developer.mozilla.org/en-US/docs/Web/API/Performance/navigation - Mister Vanderbilt
1
这段代码在弃用之后仍然有效,但我不确定它是否跨浏览器兼容性:if (String(window.performance.getEntriesByType("navigation")[0].type) === "back_forward") { // 在此处编写你的代码 } - Mladen Adamovic
3
我不在乎。对我来说,它有效,很好!2020年12月 - Cătălin Rădoi
1
弃用!!阅读此内容以获取更多信息:https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceNavigation/type - Shurvir Mori
显示剩余3条评论

15

我的变量:

const isVisitedViaBackButton = performance && performance.getEntriesByType( 'navigation' ).map( nav => nav.type ).includes( 'back_forward' )

1
这对我很有帮助。谢谢! - maverabil
回到顶部,你是页面上最好的答案。@return bool true || false - Aaron
这总是返回false,不起作用。 - Khris Vandal

15

这肯定有效(用于检测后退按钮点击)

$(window).on('popstate', function(event) {
 alert("pop");
});

最佳和最快的答案! - mxrked
2
仅仅那段代码本身是无法工作的。 - MeSo2
@rohan parab,你真勇敢啊,敢说“绝对没问题”,可是在我的Chrome上却不起作用。 - greendino

7

请看这个:

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

它正常工作...


1
在Chrome 78.0.3904.70(官方版本)(64位)中无法工作。 - Jeffz
已确认在 Brave v1.3.118 上工作正常 >> Chromium: 80.0.3987.116 (官方版本) (64 位) - ZeroThe2nd
如果您是从外部网站进入该页面,则无法正常工作。 - Marketa Vlach

6

浏览器: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>


这并没有回答所问的问题。问题是关于检测页面内返回按钮与浏览器原生返回按钮的使用。 - Xarus
3
问题标记为 JavaScript,而不是 jQuery。 - skwny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接