浏览器“后退”、“前进”或可以这么去监听

我们知道,浏览器实现了onbeforeunloadonunload事件,onbeforeonload事件是在浏览器即将请求下一个页面(请求还未发出)的时候触发,它可以实现阻止onunload的触发。onunload事件则是浏览器已经将下一个页面请求回来,页面即将跳转的时候触发,该事件无法中断。看起来onbeforeunload事件似乎能满足我们的需求,但是,这只是一个假象。

onbeforeunload事件虽然能阻止onunload事件的触发,但是由于它是浏览器内置的事件,其出现的交互方式和UI界面,均由浏览器厂商控制,并未提供给开发者定义浮层内部内容更多交互的接口,甚至文本性质的提示内容也无法设置样式。所以,想要通过onbeforeunload事件提供的浮层实现收集用户离开的原因或让用户给应用打分的功能并不现实。

下面我就详细描述下我做的思路,不过我要先声明以下几点:

  • 该方案只能部分解决需求,并不能完美解决问题
  • 这只是一种尝试,并未正式应用于业务
  • 该方案涉及history.pushState方法、popstate事件以及功臣hashchange事件

在进入主题之前,我们先来罗列几个小知识点:

  • 浏览器离开一个页面,意味着链接地址(不含hashchange、pushState方式)发生变化
  • history.pushState可以改变地址栏链接地址,但不触发页面刷新(不离开)
  • hash变化会触发popstate事件和hashchange事件
  • popstate事件对象可以获得pushState传递进去的state属性,从而得到变化后的链接地址等
  • hashchange事件对象中包含变化前后的链接地址(oldURL和newURL)
  • 浏览器的“前进”、“后退”可以触发hashchange事件

我首先想到的是,当页面加载完成时,通过status变量标记页面状态为0。利用代码push一个链接到history中,status状态改为1,标记此时链接变化了,但页面并未刷新。当用户点击浏览器“后退”按键的时候,浏览器地址首先返回页面的原始链接地址,页面并不会刷新,此时触发popstate事件,只需在事件函数中判断status === 1时出现弹层即可:

 1 var status = 0,
 2     // 存储浮层节点
 3     pop = document.getElementById(‘J_PageWrap‘);
 4 window.addEventListener(‘load‘, function() {
 5     var tit = document.title,
 6         path = location.href.replace(/#.*$/, ‘‘) + ‘#!hash‘;
 7     // 将追加了hash的链接推入history中
 8     history.pushState({title: tit, path: path}, tit, path);
 9     status = 1;
10 });
11 window.addEventListener(‘popstate‘, function(ev){
12     if (status == 1) {
13         status = 0;
14         pop.className += ‘ show‘; // show为显示浮层样式
15     }
16 });

到这里,我们的基本功能实现了:用户进入页面后,第一次点击“回退”并不会离开页面,而是触发弹层,再次点击“回退”离开当前页面。

但是,新的问题出现了。如果页面中有其他hash锚点被点击的时候,页面不会跳转,但会触发popstate事件,此时浮层便会显示,但此时用户并没有离开页面,并且如果没有在浮层中添加隐藏浮层和重置status变量的逻辑,浮层将一直显示。

于是,我开始寻找如何判断popstate触发是从初次添加的hash链接跳回页面原始链接的方法。因为,如果不是页面onload的时候,用脚本pushState添加加了hash的链接,此时页面已经回退跳出了。所以,我开始尝试从popstate事件的事件对象中寻找链接的变化线路:

但是,很遗憾!我只从对象中发现了进入页面是通过pushState传入的state属性,并没有其他任何特征属性可以帮助到我。而单看这个属性,想要判断页面链接的变化情况,实在是太难了。至少要知道现在是什么,将要变成什么,才能有判断的可能,所以,我还需要找到另一个辅助数据。

我们知道,当页面hash变化的时候,还会触发hashchange事件。那么,在hashchange的时候,有没有什么可用的数据呢?

于是,我又给页面绑定了hashchange事件,来观察hashchange带来的变化:

window.addEventListener(‘hashchange‘, function(ev){
    console.log(ev);
});

本来只是想在popstate的基础之上,通过hashchange挖掘到另一个可用的数据,却没想到有了意外的发现:

hashchange的时间对象中,竟然内置了变化前(oldURL)后(newURL)的两个链接地址。这样一来,popstate的那段逻辑,在这里似乎就没那么必要了。于是,我将代码改造成了这样:

var pop = document.getElementById(‘J_PageWrap‘);
window.addEventListener(‘load‘, function() {
    var tit = document.title,
        path = location.href.replace(/#.*$/, ‘‘) + ‘#!hash‘;
    history.pushState({title: tit, path: path}, tit, path);
});
window.addEventListener(‘hashchange‘, function(ev){
    var oAddr = ev.oldURL.replace(/^.+(?=\/\/)/, ‘‘), // 为避免http(s)的影响,去除协议进行判断
        nAddr = ev.newURL.replace(/^.+(?=\/\/)/, ‘‘);
    if (oAddr === ‘//10.14.132.43:808/tests/hash/index.html#!hash‘
        && nAddr === ‘//10.14.132.43:808/tests/hash/index.html‘) {
        pop.className += ‘ show‘;
    } else {
        pop.className = ‘page-wrap‘;
    }
});

当且仅当链接从带有#!hash返回页面原始链接的时候,设置浮层显示,否则浮层隐藏,这样就有比前面popstate的实现又进了一步。

至此,我们不仅保证了页面的正常操作,也实现了当用户点击浏览器“后退”按钮至即将离开页面的时候出现浮层,收集信息的需求。但是,还有很多问题仍然存在:

  • 如果用户进入过其他页面,再返回当前页面点击“前进”按钮的时候,并不能触发浮层
  • 在带有#!hash的时候,强制刷新页面也有可能导致“后退”路径异常
  • 直接关闭浏览器也是没办法咯

示例DEMO:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width,initial-scale=1">
 6     <title>HASHCHANGE</title>
 7 <style>
 8     html, body {
 9         margin: 0;
10         padding: 0;
11         height: 100%;
12         overflow: hidden;
13     }
14     .page-wrap {
15         position: absolute;
16         top: 100%;
17         height: 30%;
18         width: 100%;
19         font-size: 250px;
20         line-height: 30vh;
21         text-align: center;
22         background-color: #f00;
23         color: #fff;
24         -webkit-transition: top .3s ease-out;
25         -o-transition: top .3s ease-out;
26         transition: top .3s ease-out;
27     }
28     .page-wrap.show {
29         top: 70%;
30     }
31     .page-main {
32         text-align: center;
33         line-height: 100vh;
34         font-size: 10vw;
35         font-weight: 600;
36     }
37 </style>
38 </head>
39 <body>
40     <div class="page-main">
41         <a href="#changeHash" target="_self">SecondPage</a>
42     </div>
43     <div id="J_PageWrap" class="page-wrap">0</div>
44 <script>
45 (function(){
46     var status = 0,
47         pop = document.getElementById(‘J_PageWrap‘);
48     window.addEventListener(‘load‘, function() {
49         var tit = document.title,
50             path = location.href.replace(/#.*$/, ‘‘) + ‘#!hash‘;
51         history.pushState({title: tit, path: path}, tit, path);
52         status = 1;
53     });
54     // window.addEventListener(‘popstate‘, function(ev){
55     //     console.log(ev);
56     //     if (status == 1) {
57     //         status = 0;
58     //         pop.className = ‘page-wrap show‘;
59     //     }
60     // });
61     window.addEventListener(‘hashchange‘, function(ev){
62         // console.log(ev);
63         var oAddr = ev.oldURL.replace(/^.+(?=\/\/)/, ‘‘),
64             nAddr = ev.newURL.replace(/^.+(?=\/\/)/, ‘‘);
65         if (oAddr === ‘//seejs.com/demos/examples/goback/index.html#!hash‘
66             && nAddr === ‘//seejs.com/demos/examples/goback/index.html‘) {
67             pop.className += ‘ show‘;
68         } else {
69             pop.className = ‘page-wrap‘;
70         }
71     });
72 })();
73 </script>
74 </body>
75 </html>

转载至原文:http://web.jobbole.com/89526/

时间: 2024-10-01 22:21:26

浏览器“后退”、“前进”或可以这么去监听的相关文章

微信浏览器返回刷新,监听微信浏览器返回事件,网页防复制

以下代码都经过iphone7,华为MT7 ,谷歌浏览器,微信开发者工具,PC端微信验证.如有bug,还请在评论区留言. demo链接:https://pan.baidu.com/s/1c35mbjM 密码:5yyf 1.移动端微信浏览器返回刷新事件,在返回后的页面上加上以下代码: <script type="text/javascript"> $(function () { var isPageHide = false; window.addEventListener('p

从网页监听Android设备的返回键

最近搞Android项目的时候,遇到一个比较蛋疼的需求,需要从Client App调用系统浏览器打开一个页面,进行杂七杂八的一些交互之后,返回到App.如何打开浏览器和如何返回App这里就不说了,有兴趣的童鞋可私下交流. 之所以说这个需求蛋疼,是因为Android有个物理返回键啊……返回键啊……键啊……啊…… 用户按下返回键后,预期应该跟点击页面上的返回键一样——返回App.然而这个返回键的被按下的时候网页完全不知道啊(onbeforeunload不算),找不到直接的办法去监听,愁死我们这全苦逼

Android NDK开发(八)——应用监听自身卸载,弹出用户反馈调查

转载请注明出处:http://blog.csdn.net/allen315410/article/details/42521251 监听卸载情景和原理分析 1,情景分析 在上上篇博客中我写了一下NDK开发实践项目,使用开源的LAME库转码MP3,作为前面几篇基础博客的加深理解使用的,但是这样的项目用处不大,除了练练NDK功底.这篇博客,我将讲述一下一个各大应用中很常见的一个功能,同样也是基于JNI开发的Android应用小Demo,看完这个之后,不仅可以加深对NDK开发的理解,而且该Demo也可

oninput和onpropertychange实时监听输入框值的变化

传统监听输入框的做法就是使用keyup.keydown.keypress,或者change事件来实现,但keyup.keydown.keypress事件是只要完成击键事件后就触发,不考虑输入框的值是否变化,也监听不了使用鼠标右键[剪贴]和[粘贴]这些操作,更监听不了使用JS动态改变值的变化.而change事件必须是焦点离开输入框后才触发,并不能实时监听.所以这几个事件来监听输入框值变化并不完美.ie浏览器(ie6-8)可以直接使用onpropertychange事件来实时监听(包括JS动态改变值

记录一次处理https监听不正确的过程

今天开发反馈在测试金山云设备的时候遇到了这样的一个现象: wget https://funchlscdn.lechange.cn/LCLR/2K02135PAK01979/0/0/20170726085033/dev_20170726085033_lpxh73ezzb92xxa8.m3u8  --2017-07-26 11:49:26--  https://funchlscdn.lechange.cn/LCLR/2K02135PAK01979/0/0/20170726085033/dev_201

原生js监听input值改变事件

哈哈哈,又来了,今天闲来无事,实验了下原生js监听input value值改变事件,下面就来说道说道: 本来写监听input值便获是用jquery的,之前的随笔写了,就是这个方法,地址:http://www.cnblogs.com/wteng/p/5434403.html $('input').bind('input propertychange', function() { //to do }) 现在用原生js来实现一遍(其实我翻了下jquery的监听事件on的源码,没找到不知道他写哪了),本

Android NDK开发(九)——应用监听自身卸载升级版,使用Inotify监听安装目录

转载请注明出处:http://blog.csdn.net/allen315410/article/details/42555415 在上一篇博客中,我们讲了一个小小的案例,用NDK监听应用程序自身卸载,并且打开内置浏览器加载用户调用页面.关于监听应用程序自身卸载的原理和实现方案可以在上篇博客中找到,地址是:http://blog.csdn.net/allen315410/article/details/42521251,这里就不再复述了. 值得注意的是,在上篇博客中我也已经引述了一个案例中存在的

完美解决onchange不能实时的监听

我们大家都知道onchange有时候很不好用,因为onchange事件是离开焦点后才会被触发,而不是实时去监听! 那么oninput()事件和onpropertychange()完美的解决了问题:(onpropertychange是IE版本的专利) 在jquery中只有IE8和IE8以下的低版本支持: Obj.bind("propertychange", function () {}); 其他浏览器可用: Obj.bind("input", function ()

简单剖析Node中的事件监听机制(一)

使用js的class类简单的实现一个事件监听机制,不同于浏览器中的时间绑定与监听,类似于node中的时间监听,并且会在接下来的文章中去根据自己的理解去写一下Event模块中的原理. Node.js使用了一个事件驱动.非阻塞式I/O的模型,使其轻量又高效.并且Node中的大量模块都使用了Event机制,因此可以说是整个Node中最重要的模块之一. 实例: let event = new eventEmitter(); event.on('someType',function(){ }); even