Window: popstate 事件

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

当用户浏览会话历史记录时,活动历史记录条目发生变化时,Window 接口的 popstate 事件会被触发。它会将当前历史记录条目更改为用户访问的上一页的条目,或者,如果使用 history.pushState() 将历史记录条目添加到历史记录堆栈中,则会使用该历史记录条目。

语法

在诸如 addEventListener() 之类的方法中使用事件名称,或设置事件处理程序属性。

js
addEventListener("popstate", (event) => { })

onpopstate = (event) => { }

事件类型

一个 PopStateEvent。继承自 Event

Event PopStateEvent

事件属性

PopStateEvent.state 只读

返回提供给 pushState()replaceState() 的信息的副本。

事件处理程序别名

除了 Window 接口,以下元素也提供 onpopstate 事件处理程序属性

历史记录堆栈

如果通过调用 history.pushState() 创建了正在激活的历史记录条目,或者受到调用 history.replaceState() 的影响,则 popstate 事件的 state 属性包含历史记录条目状态对象的副本。

这些方法及其相应的事件可用于向历史记录堆栈添加数据,这些数据可用于重建动态生成的页面,或在保持同一 Document 的同时更改所呈现内容的状态。

请注意,仅仅调用 history.pushState()history.replaceState() 不会触发 popstate 事件。popstate 事件将通过执行浏览器操作来触发,例如单击后退或前进按钮(或在 JavaScript 中调用 history.back()history.forward())。

浏览器在页面加载时处理 popstate 事件的方式往往不同。Chrome(v34 之前)和 Safari 始终在页面加载时触发 popstate 事件,但 Firefox 不会。

注意:在编写处理 popstate 事件的函数时,务必考虑 window.location 等属性已经反映了状态变化(如果它影响了当前 URL),但 document 可能尚未反映。如果目标是捕获新文档状态已完全就绪的时刻,则应使用零延迟 setTimeout() 方法调用,以有效地将其内部处理的回调函数放在浏览器事件循环的末尾:window.onpopstate = () => setTimeout(doSomeThing, 0);

popstate 何时发送

首先理解,为了对抗不需要的弹出窗口,除非页面已经进行过交互,否则浏览器可能根本不会触发 popstate 事件,这一点很重要。

本节描述了浏览器在确实可能触发 popstate 事件的情况下(即页面已进行交互的情况下)所遵循的步骤。

当发生导航时——无论是由于用户触发浏览器的 后退 按钮还是其他原因——popstate 事件是在导航到新位置的过程接近尾声时发生的。它发生在加载(如果需要)、显示、可见等新位置之后——在发送 pageshow 事件之后,但在恢复持久用户状态信息和发送 hashchange 事件之前。

为了更好地理解 popstate 事件何时触发,请考虑当当前历史记录条目由于用户导航网站或通过编程遍历历史记录而更改时发生的简化事件序列。在这里,转换正在将当前历史记录条目更改为我们将其称为 new-entry 的条目。当前页面的会话历史记录堆栈条目将被称为 current-entry

  1. 如果 new-entry 当前不包含现有的 Document,则在继续之前获取内容并创建其 Document。这最终会向包含文档的 Window 发送诸如 DOMContentLoadedload 等事件,但在此期间,以下步骤将继续执行。
  2. 如果 current-entry 的标题未使用历史记录 API 方法之一(pushState()replaceState())设置,则将其标题设置为其 document.title 属性返回的字符串。
  3. 如果浏览器在离开 current-entry 之前希望存储与 current-entry 相关的状态信息,它会这样做。该条目现在被称为具有“持久用户状态”。浏览器可能添加到历史记录会话条目的信息可能包括,例如,文档的滚动位置、表单输入的 S值以及其他此类数据。
  4. 如果 new-entry 具有与 current-entry 不同的 Document 对象,则更新浏览上下文,使其 document 属性引用 new-entry 引用的文档,并且更新上下文的名称以匹配当前文档的上下文名称。
  5. new-entryDocument 中每个将 autocomplete 配置为自动填充字段名称设置为 off 的表单控件都将被重置。有关自动填充字段名称以及自动填充工作原理的更多信息,请参阅 HTML autocomplete 属性
  6. 如果 new-entry 的文档已完全加载并准备就绪——也就是说,它的 readyStatecomplete——并且文档尚未可见,则使其可见,并向文档触发 pageshow 事件,其中 PageTransitionEventpersisted 属性设置为 true
  7. 文档的 URL 设置为 new-entry 的 URL。
  8. 如果正在执行的遍历历史记录已启用替换,则目标条目紧接之前的条目(考虑到 go() 等方法上的 delta 参数)将从历史记录堆栈中删除。
  9. 如果 new-entry 没有持久用户状态,并且其 URL 的片段非 null,则文档滚动到该片段。
  10. 接下来,current-entry 设置为 new-entry。目标条目现在被认为是当前条目。
  11. 如果 new-entry 保存了序列化状态信息,则该信息被反序列化到 History.state 中;否则,statenull
  12. 如果 state 的值发生变化,则向文档发送 popstate 事件。
  13. 如果浏览器选择这样做,则恢复任何持久用户状态。
  14. 如果原始条目和新条目共享相同的文档,但其 URL 中具有不同的片段,则向窗口发送 hashchange 事件。

如你所见,popstate 事件是这种页面导航过程中几乎最后完成的事情。

示例

运行以下代码的 http://example.com/example.html 页面将按指示生成日志

js
window.addEventListener("popstate", (event) => {
  console.log(
    `location: ${document.location}, state: ${JSON.stringify(event.state)}`,
  );
});
history.pushState({ page: 1 }, "title 1", "?page=1");
history.pushState({ page: 2 }, "title 2", "?page=2");
history.replaceState({ page: 3 }, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null"
history.go(2); // Logs "location: http://example.com/example.html?page=3, state: {"page":3}"

使用 onpopstate 事件处理程序属性的相同示例

js
window.onpopstate = (event) => {
  console.log(
    `location: ${document.location}, state: ${JSON.stringify(event.state)}`,
  );
};
history.pushState({ page: 1 }, "title 1", "?page=1");
history.pushState({ page: 2 }, "title 2", "?page=2");
history.replaceState({ page: 3 }, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null"
history.go(2); // Logs "location: http://example.com/example.html?page=3, state: {"page":3}"

请注意,即使原始历史记录条目(针对 http://example.com/example.html)没有与其关联的状态对象,但在第二次调用 history.back() 之后激活该条目时,仍然会触发 popstate 事件。

规范

规范
HTML
# event-popstate
HTML
# handler-window-onpopstate

浏览器兼容性

另见