防止 URL 在 popstate 发生时改变

Prevent the URL from changing when popstate occurs

您好,我正在尝试实现一个 "Are you sure you want to leave this page?" 弹出窗口,当导航离开具有尚未提交的已修改表单的页面时。该网站是使用自定义框架构建的单页应用程序。

由于所有锚点 link 都由框架处理,因此它可以判断单击侧导航 link 将导致页面更改并显示 Bootstrap 确认对话框。如果用户单击 "Ok",则单击将通过,并进行 AJAX 调用以拉取新页面内容。如果他们点击 "Cancel" 模式将被关闭,他们将停留在当前页面。

我未能解决的部分是当用户在其网络浏览器中单击 "Back" 按钮时。这会触发 "popstate" 事件。所有 navigation/history 都使用历史 API 进行管理,因为它是一个单页应用程序。

问题是当有人点击 "Back" 时,URL 发生了 "popstate" 的变化,然后我显示了我的 "Are you sure you want to leave?" 模态。当事件发生时,地址栏中的 URL 现在会显示您将要从历史记录转到的页面,而不是您当前正在查看的页面。如果您单击 Bootstrap 中的 "Cancel" 确认,您现在在地址栏中显示的是错误的 URL。

popstate 中的

event.preventDefault、event.stopPropagation 或 return false 不会阻止 URL 更改。据我所知,您无法拦截 "popstate" 之前的 "Back" 按钮。尝试在 "popstate" 中 window.history.replaceState 似乎也不起作用。

有没有人有关于如何使这项工作的解决方案?

  1. 你能阻止 URL 在 "popstate" 事件中改变吗?
  2. 你能否在 "popstate" 中以某种方式重写历史记录,将 URL 改回当前页面上的内容,并保留之前的条目,如果你决定要它在之后通过模式已被接受?
  3. 我没有想到的其他解决方案?

我最初的想法是阻止 URL 更改 "popstate" 然后让框架触发 link 点击加载新内容并更改 URL if他们点击 "Yes" 但我还没有找到方法。

谢谢!

我通过执行以下操作解决了这个问题;

  1. 每次创建历史状态时,我都会生成一个时间戳作为 guid
  2. 当前时间戳存储在顶层模块的一个变量中
  3. 当 popstate 发生时,将传入历史状态的 guid 与顶级模块 guid 进行比较
  4. 如果新的 guid 更大,我们就前进,如果它更小,我们就后退
  5. 当用户单击 back/forward 按钮时,哈希值会更改为传入的 URL。如果用户单击取消,它会运行 history.go( direction ),其中 direction 为 1 或 -1,具体取决于时间戳。这会将 URL 设置回它应该的状态并且不会对历史堆栈做任何奇怪的事情。顶级历史变量有一个标志,可以知道我们正在伪造页面更改,因此不会执行 popstate 中的 link 加载逻辑。

唯一的问题是,如果用户在发送和返回该请求并创建历史记录对象时确实单击“是”导航离开,则您不会更新顶级模块的 GUID。如果您执行 guid 比较,将始终认为您正在倒退,因为您刚刚添加的新事件将始终是最高数字。如果您不对历史记录 URL 发出新请求,这可能不是问题,但我们的框架会这样做,因此它不会像浏览器通常那样显示陈旧数据。

它并不理想,因为您确实看到了 URL 变化(没有内容变化),但到目前为止它似乎运行良好。我发现的另一个解决方案是将您自己站点的历史记录存储在 local/session 存储中并比较 URLs 而不是使用 guid 时间戳来查找 popstate 方向。这在 Safari 的隐私浏览模式下不起作用,因为两个存储层都被禁用,所以我选择了 guid。