【问题标题】:Custom back button based on history基于历史的自定义后退按钮
【发布时间】:2013-06-07 22:15:17
【问题描述】:

在我的单页网络应用程序中,我希望在某些页面上有一个后退按钮,可以将用户带回到浏览器历史记录中的特定点:不一定是上一页。

请注意,我不是在谈论浏览器的后退按钮,它应该像往常一样工作。

用例

例如,考虑 gmail 网络应用程序。有一个文件夹视图,可能带有搜索过滤器或其他参数,然后是一个显示单个消息的消息视图。在消息视图中,有一个后退按钮不会将您带到上一页:即使您单击了几条消息,后退按钮也会将您带回到原来的文件夹视图,然后再单击任何的消息。

在 gmail 的情况下,这可以通过简单地在 URL 末尾编码之前的状态来实现。例如,如果您通过搜索找到此消息,它将是:

#search/stuff/<MESSAGE_ID>

由此,应用知道后退按钮应该指向页面:

#search/stuff

然而,在我的应用程序中,有太多的状态无法将其全部编码到 URL 中。不是两个视图(文件夹 + 消息),而是三个视图,我们称它们为 A、B 和详细信息,其中 A 和 B 都有大量可能的过滤器和编码在 URL 中的选项,我想在何时保留这些过滤器和选项例如从 B 到 A 或从细节返回到 B。在细节页面的 URL 中对 A、B 和细节的所有参数进行编码会使 URL 非常笨拙。

实施

我认为这可以使用 html5 历史 API 轻松实现。但是,据我所知,历史 API 不支持读取历史中的 URL:您只能盲目地向后或向前。

history.js 提供对过去状态的访问,如相关问题中所述:

History API: Javascript Pushstate, get previous URL

但是,我使用的 angularjs 不适用于 history.js,因为它直接与 history api 对话,而不是通过 history.js,所以由 angular $location 服务引起的转换状态不会显示在 history.js 的 History 对象中。

看来我需要做以下事情之一:

  • 让 history.js 与 angular 完美配合
  • 在我自己的代码中重新实现 history.js 的一部分功能

我还考虑使用 document.referrer 来查看历史记录中的前一个值,但这不起作用,因为在单页应用程序中导航时它没有设置。

【问题讨论】:

  • 你可以添加你自己的 onpopstate 或 onhashchange 来记录每一个 url 的变化,不管是谁做的。然后你有一个过去的 URL 列表,就像历史一样。此外,使用原生 pushState,您可以将大状态与 url 关联而不对其进行编码,它与 url 的参数不同。
  • @dandavis:我已经做到了,但是我在哪里存储这些信息?如果我不存储它,整个页面重新加载将丢失历史记录。如果我将它存储在 sessionStorage 中,并且用户在两个选项卡中打开我的页面,我如何避免两个选项卡的历史相互干扰?
  • 好的,看来我可以简单地使用 sessionStorage,因为快速测试确认 sessionStorage 不是跨选项卡共享的。
  • 您不必对 URL 中的所有数据进行编码。只需使用与对象文字中的键匹配的 ID,该对象文字包含特定状态所需的所有内容。
  • @dandavis:另外,onpopstate/onhashchange 并没有告诉我历史上是要倒退还是前进……

标签: javascript angularjs history.js html5-history


【解决方案1】:

回答我自己的问题,我选择了@MaxArt 在他的评论中建议的更简单的解决方案。

  • 为页面生成随机 ID
    • 由于我使用 angularjs,所以我这样做 $on('$routeChangeSuccess') 或 $on('$routeUpdate')
    • 如果没有 angularjs,我想我会在 onpopstate 上执行此操作
  • 在 sessionStorage 中存储从这个随机 ID 到我需要的所有 URL 信息(在我的例子中是搜索和路径)的映射
  • 在我的应用的概念层次结构中向前的传出链接中包含一个搜索参数 from=当前页面的 ID
  • 单击自定义后退按钮时,使用 from seach 参数在 sessionStorage 中查找我来自的状态
    • 如果找到,请返回该 URL
    • 如果未找到(用户直接导航到此页面),请返回应用层次结构中上一个视图的默认 URL

采用这种方法而不是建立过去 URL 历史的更一般方法的理由:

  • 如前所述,pushState API 不提供对过去 URL 的访问权限
  • 将提供该信息的 History.js 集成到 angularjs 中似乎并非易事
    • angularjs 在内部使用 history API:需要改为使用 History
  • 实现自定义代码以在 sessionStorage 中记录 URL 历史记录也并非易事。主要问题是,由于缺乏与浏览器历史的集成,似乎没有一种通用的方法可以知道访问的页面是新的还是通过返回浏览器历史中的一个或多个步骤来访问的。如果有人可以提出解决方案,我很乐意对此进行更正 (1)
  • 使用 History.js 或任何使用附加代码包装所有 history.pushState 的等效解决方案,需要将页面上的几乎所有链接都包装在 History.pushState 中,否则 URL 更改不会显示在历史记录中。李>

(1)How do I retrieve if the popstate event comes from back or forward actions with the HTML5 pushstate?

【讨论】:

    【解决方案2】:

    我从未使用过 angular.js,但如果您希望某些东西可以保证工作,大概 window.history.pushState 就是您正在寻找的东西。阅读http://spoiledmilk.com/blog/html5-changing-the-browser-url-without-refreshing-page/ 了解有关此婴儿可以做什么的详细信息。

    【讨论】:

    • 如问题中所述,pushState API 不提供对过去 url 的访问。所以我可以后退一步,或者后退很多,但我不能“在 url 匹配此模式之前返回”
    • true,但您只使用 pushState 告诉浏览器向堆栈添加历史记录条目。您使用 history.go(-1) 导航“返回”,无论您使用 pushState 添加了什么内容,它都应该可以工作
    • 我明白,但我不想返回 1。我想返回我的历史记录中第一个匹配条件的 URL,如果没有,请转到特定页面。 history.go() 无法做到这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 2015-10-11
    • 2020-05-12
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多