当前位置:   article > 正文

从一个需求说起,H5实现万能返回

前端h5返回上一个页面的回调

H5开发返回上一页是一个很辣手的问题。开发中,我们一般用history.go(-1)返回上一页,原生返回按钮的动作也是history.go(-1)。由于业务需求,某些页面返回需要go(-2)或go(-3)、go(-n),有些页面返回时还有加一个弹窗,让用户再次确认是否离开,这些都有监听物理机的返回事件。开发时,思维打结了,没想到“监听物理机的返回事件”这关键句子,踩了挺多坑的,本文就是记录一下小编的踩坑过程。

前几天就遇到了一个需求,离开正在编辑的页面时,弹窗提示用户,尚未保存编辑信息,是否确定离开。确定则离开,取消则留下继续编辑。当时觉得很简单,调用window.onbeforeunload便可,很快实现了这个需求。

beforeunload

  1. // beforeunload实现,优点:简单,缺点:弹窗不能自定义,而且兼容性不好
  2. window.addEventListener('beforeunload', (event) => {
  3. // Cancel the event as stated by the standard.
  4. event.preventDefault();
  5. // Chrome requires returnValue to be set.
  6. event.returnValue = '';
  7. });


beforeunload的缺点太多,局限性太大,个人觉得不是一个好的方案,于是有了下面的踩坑。

history

由于所有的返回上一页都是调用history.go(-1)。于是去百度go(-1)的监听事件popstate,然后在监听回调里让自定义弹窗展示。思路是没错,可弹窗没弹出来。于是在进入页面前调localStorage加了个缓存,在监听的回调里把缓存清除,测试回调是否执行。是的,回调没有执行,心里一千个草泥马在奔腾,一度怀疑这api是假的。

于是把MDN文档的例子复制过来,原来是思维打结了。

  1. window.addEventListener('popstate', (event) => {
  2. console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
  3. });
  4. history.pushState({page: 1}, "title 1", "?page=1");
  5. history.pushState({page: 2}, "title 2", "?page=2");
  6. history.replaceState({page: 3}, "title 3", "?page=3");
  7. history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
  8. history.back(); // Logs "location: http://example.com/example.html, state: null
  9. history.go(2); // Logs "location: http://example.com/example.html?page=3, state: {"page":3}

由于小编开发是多页应用,go(-1)就直接返回上一页了,没触发监听。要触发监听,进入页面后必须使用了history.pushState()(该api可以改变浏览器历史记录,可以改变URL还不会触发页面重新请求。)添加一条自定义历史记录。这时再调go(-1),浏览器检测不到url变化,就不会重载页面,这时监听才能触发。

  1. function doCcallback() {
  2. // 自定义弹窗
  3. var r = confirm("是否退出");
  4. if (r == true) {
  5. history.go(-1);
  6. } else {
  7. addState();
  8. }
  9. }
  10. // 监听浏览器返回
  11. window.addEventListener("popstate", function (e) {
  12. if (history.state === null || history.state._is !== "first") {
  13. doCcallback();
  14. }
  15. });
  16. // 使用异步,目的是等待浏览器历史记录发生变化后在添加自定义记录,这样自定义记录会准确添加到栈顶
  17. setTimeout(function () {
  18. if (history.state === null || history.state._is !== "first") {
  19. addState();
  20. }
  21. }, 300);
  22. function addState() {
  23. // 添加浏览器历史记录,url就使用当前的页面的url
  24. history.pushState({ _is: "first" }, "", location.href);
  25. }

查了一下history.pushState的兼容性,兼容性也还可以。有时候就这么神奇,突然脑袋又多了另一个想法,也可以添加历史记录不触发浏览器请求。

hash

  1. function doCcallback() {
  2. // 自定义弹窗
  3. var r = confirm("是否退出");
  4. if (r == true) {
  5. history.go(-1);
  6. } else {
  7. addState();
  8. }
  9. }
  10. // 监听路由hash值是否改变
  11. window.addEventListener("hashchange", function (e) {
  12. // 判断存在hash值才进入
  13. if ("#goback" !== location.hash) {
  14. doCcallback();
  15. }
  16. });
  17. // 使用异步,目的是等待浏览器历史记录发生变化后在添加自定义记录,这样自定义记录会准确添加到栈顶
  18. setTimeout(function () {
  19. if ("#goback" !== location.hash) {
  20. addState();
  21. }
  22. }, 300);
  23. function addState() {
  24. // 添加浏览器历史记录
  25. location.hash = "goback";
  26. }

该方法优点:兼容性好,缺点:在SPA应用中可能会冲突。有时候就这么神奇,突然脑袋又多了另一个想法。记得vue-router使用了两种路由实现方式,供用户选择。

history and hash

  1. function MonitorBack(config) {
  2. if (Object.prototype.toString.call(config) !== "[object Object]") {
  3. config = {};
  4. }
  5. if (Object.getPrototypeOf(this).constructor !== MonitorBack) {
  6. return new monitorBack();
  7. }
  8. var mode = config.mode || "hash"; // 模式
  9. var self = this;
  10. var history = window.history;
  11. var location = window.location;
  12. var methods = {
  13. // 存储事件回调
  14. goback: [],
  15. };
  16. if (mode === "history") {
  17. historyMode();
  18. } else {
  19. hashMode();
  20. }
  21. this.getMode = function setMode() {
  22. return mode;
  23. };
  24. // 返回上一页
  25. this.back = function back() {
  26. var backCount = -1;
  27. if (mode === "history") {
  28. backCount = history.state && history.state._is === "first" ? -2 : -1;
  29. } else {
  30. backCount = location.hash === "#goback" ? -2 : -1;
  31. }
  32. history.go(backCount);
  33. };
  34. // 停留
  35. this.stay = function stay() {
  36. history.forward();
  37. };
  38. // 监听
  39. this.on = function on(key, cb) {
  40. if (typeof key !== "string") {
  41. return;
  42. }
  43. if (typeof cb !== "function") {
  44. return;
  45. }
  46. var method = methods[key];
  47. method.push(cb);
  48. };
  49. // 取消监听
  50. this.off = function off(key, cb) {
  51. if (typeof key !== "string") {
  52. return;
  53. }
  54. if (typeof cb !== "function") {
  55. return;
  56. }
  57. var method = methods[key] || [];
  58. for (var i = 0, l = method.length; i < l; i++) {
  59. if (method[i] === cb) {
  60. method.splice(i, 1);
  61. break;
  62. }
  63. }
  64. };
  65. function historyMode() {
  66. // history 模式
  67. setTimeout(function () {
  68. if (history.state === null || history.state._is !== "first") {
  69. history.pushState({ _is: "first" }, "", location.href);
  70. }
  71. }, 300);
  72. window.addEventListener("popstate", function (e) {
  73. if (history.state === null || history.state._is !== "first") {
  74. var method = methods.goback || [];
  75. for (var i = 0, l = method.length; i < l; i++) {
  76. method[i].call(self);
  77. }
  78. }
  79. });
  80. }
  81. function hashMode() {
  82. // hash模式
  83. setTimeout(function () {
  84. if ("#goback" !== location.hash) {
  85. location.hash = "goback";
  86. }
  87. }, 300);
  88. window.addEventListener("hashchange", function (e) {
  89. // 判断存在hash值才进入
  90. if ("#goback" !== location.hash) {
  91. var method = methods.goback || [];
  92. for (var i = 0, l = method.length; i < l; i++) {
  93. method[i].call(self);
  94. }
  95. }
  96. });
  97. }
  98. }
  99. var monBack = new MonitorBack({ mode: "history" });
  100. monBack.on("goback", function () {
  101. var r = confirm("是否退出");
  102. if (r == true) {
  103. this.back();
  104. } else {
  105. this.stay();
  106. }
  107. });
  1. ❤️爱心三连击1.看到这里了就点个在看支持下吧,你的「点赞,在看」是我创作的动力。
  2. 2.关注公众号程序员成长指北,回复「1」加入Node进阶交流群!「在这里有好多 Node 开发者,会讨论 Node 知识,互相学习」!
  3. 3.也可添加微信【ikoala520】,一起成长。
  4. “在看转发”是最大的支持
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号