如果popstate事件来自HTML5 pushstate的后退或前进操作,如何检索?

Dav*_*ket 57 javascript html5 browser-history pushstate

我正在开发一个网页,根据我执行相应动画的下一步或后退动作,使用pushstate时会出现问题.当我收到活动时,如何知道用户是否使用Pushstate API点击了后退或转发历史记录按钮?或者我是否必须自己实施某些内容?

ben*_*ich 59

你必须自己实现它,这很容易.

  • 调用时pushState为数据对象提供唯一的递增id(uid).
  • onpopstate处理器会被调用; 检查状态uid是否反对包含最后一个状态uid的持久变量.
  • 使用当前状态uid更新持久变量.
  • 根据状态uid是否大于或小于最后状态uid,执行不同的操作.

  • 当页面/视图层次结构被明确定义时,这种方法很有效,并且某个页面始终位于另一个页面之后始终是正确的.当导航可以更加动态时,您不能依赖递增的UID.我已经通过使用sessionStorage维护最后X页的堆栈来解决这个问题.然后,在popstate事件上,您可以从sessionStorage检查堆栈,以查看新页面URL是否与N-2页面的URL相同.使用sessionStorage而不是普通变量,因此它在页面边界之间保持不变. (16认同)
  • @frontendbeauty我认为您应该将您的提案发布为代码片段的答案. (14认同)
  • @frontendbeauty如果你在页面上,你在中间页面b,你的堆栈看起来像这样“[a,b,a]”,你会得到“popstate”事件,你怎么知道它是后退还是前进? (3认同)
  • 另外,您可以编写状态更改管理器来检查ID以查看当前状态以及当前状态是什么,而不是检查该值是大还是小,然后执行相应的动画处理 (2认同)
  • 这很简单 - 但你忘记添加“相当简单”的代码片段:) (2认同)

Mic*_*lan 7

此答案应适用于单页推送状态应用程序、多页应用程序或两者的组合。 (更正以修复History.lengthMesqualito 评论中解决的错误。)

这个怎么运作

我们可以轻松地侦听历史堆栈的新条目。我们知道,对于每个新条目,规范要求浏览器:

  1. “在当前条目之后删除浏览上下文会话历史记录中的所有条目”
  2. “在最后添加一个新条目”

因此,在进入时:

新入场位置 = 最后显示的位置 + 1

那么解决办法是:

  1. 用它在堆栈中的位置标记每个历史条目
  2. 在会话存储中跟踪最后显示的位置
  3. 通过比较两者来发现行进方向

示例代码

function reorient() // After travelling in the history stack
{
    const positionLastShown = Number( // If none, then zero
      sessionStorage.getItem( 'positionLastShown' ));
    let position = history.state; // Absolute position in stack
    if( position === null ) // Meaning a new entry on the stack
    {
        position = positionLastShown + 1; // Top of stack

        // (1) Stamp the entry with its own position in the stack
        history.replaceState( position, /*no title*/'' );
    }

    // (2) Keep track of the last position shown
    sessionStorage.setItem( 'positionLastShown', String(position) );

    // (3) Discover the direction of travel by comparing the two
    const direction = Math.sign( position - positionLastShown );
    console.log( 'Travel direction is ' + direction );
      // One of backward (-1), reload (0) and forward (1)
}

addEventListener( 'pageshow', reorient );
addEventListener( 'popstate', reorient ); // Travel in same page
Run Code Online (Sandbox Code Playgroud)

另请参阅代码的实时副本

局限性

此解决方案忽略外部页面的历史记录条目,对于应用程序来说是陌生的,就好像用户从未访问过它们一样。它仅计算与上次显示的应用程序页面相关的行进方向,而不管其间访问过的任何外部页面。如果您希望用户将外部条目推送到堆栈上(请参阅 Atomosk 的评论),那么您可能需要一种解决方法。

  • 是的,你说得对。我永远不会习惯前端 api 有多么糟糕。检查我可以使用后退/前进按钮走多远应该是一行,但有人决定否则。 (6认同)
  • 浏览器后退/前进历史有一个固定的限制,在 Chrome 中是 50。当你达到这个限制时,`history.length` 将总是产生相同的数字(在我的例子中是 50),因此这个解决方案总是返回 0 作为方向,因为所有状态条目将具有相同的位置。 (2认同)