【问题标题】:How do I use window.history in JavaScript?如何在 JavaScript 中使用 window.history?
【发布时间】:2015-10-10 06:43:31
【问题描述】:

我在 Stack Overflow 上发现了很多关于此的问题,但它们都非常具体地针对某些部分。我确实找到了this 问题,其答案提供了一些很好的参考,但他们实际上并没有解释这一切是如何工作的,而且他们的例子几乎没有任何作用。我想了解更多关于它们如何协同工作的信息,并且我想使用 vanilla JavaScript。

(此外,其他问题的许多答案都是多年前的。)

【问题讨论】:

  • 您发布问题只是为了自己回答吗?只问是因为答案很长,并且在提出问题后立即发布 - 并且没有任何编辑!
  • 是的。我必须自己收集所有这些信息,所以我想我不妨在 Stack Exchange 上分享它。
  • @Kano ,如果我没记错的话,Stackoverflow 鼓励这种自我回答类型的问题,因为它们有助于知识共享。

标签: javascript html browser-history html5-history


【解决方案1】:

开始

首先,您可以删除window 部分。只是history 工作正常。但在我们了解一切如何协同工作之前,我们需要知道我们可以使用什么。

重要事件

window.onload

每当您的网页被加载时,此事件就会触发。有两种情况会触发此事件:

  1. 当您的网页从另一个网页导航到时。请注意,我写的是 webpage,而不是 website。在同一网站上的页面之间移动会触发此事件。
  2. 刷新网页后。

window.onpopstate

当您在已设置的历史状态之间导航时会触发此事件。您的浏览器会在正常浏览期间自动设置历史状态(为 null),但在这些状态之间导航不会触发此事件。

window.onunload

每当您的网页被卸载时都会触发此事件。有两种情况会触发此事件:

  1. 当您从自己的网页导航到另一个网页时。
  2. 就在您的网页刷新之前。

重要对象

历史接口包含五个函数(如下所述),两个只读对象(此处描述),工作方式有点像linked list。历史对象的每个“链接”中包含的两个对象是:

  • length - 这是当前浏览器窗口的历史状态数。它从 1 点开始。
  • state - 这是一个几乎可以包含任何内容的 JavaScript 对象。默认为null

您可以通过分别调用history.lengthhistory.state 来访问它们,尽管history.state 只能用于获取当前历史状态。

重要功能

history.go(距离)

此功能与在浏览器中按后退或前进按钮的作用相同,还增加了能够准确指定您想要走多远的功能。例如,history.go(3) 与按三下前进按钮具有相同的效果,而无需实际加载开始位置和结束位置之间的页面。负值同样会使您在历史记录中倒退。 history.go(0)history.go(),甚至history.go(NaN)与刷新页面的效果相同(这不会触发popstate事件)。如果您不能向前/向后移动到指定的距离,该函数将不执行任何操作。

history.back()

此功能与浏览器中的后退按钮具有相同的功能。它相当于history.go(-1)。如果无法返回,该函数将不执行任何操作。

history.forward()

此功能与浏览器中的前进按钮具有相同的功能。它相当于history.go(1)。如果它不能前进,该函数将什么也不做。

history.replaceState(state, title[, location])

这个函数替换当前的历史状态。它需要三个参数,尽管最后一个是可选的。论据是:

  • state - 这是最重要的参数。您提供给此参数的对象将保存到history.state 以供以后检索。这是一个深拷贝,因此如果您稍后修改原始对象,它不会更改保存的状态。您也可以将其设置为null,但如果您不打算使用它,那么使用history 根本没有多大意义。
  • title - HTML 标准建议传递给此参数的字符串可供浏览器在用户界面中使用,但目前没有浏览器对此进行任何处理。
  • location - 此参数允许您更改相对于当前页面的 URL。它不能用于将 URL 更改为另一个网站的 URL,但它可以用于将 URL 更改为 您的 网站上另一个页面的 URL。但是,我建议不要这样做,因为即使 URL 是另一个页面,该页面实际上也没有重新加载。使用后退/前进将显示更改后的 URL,但不会更改页面,并且会触发popstate 而不是loadunload。更改 URL 后刷新页面将加载由 URL 指定的页面,而不是您之前所在的页面。此功能可用于在当前状态下提供指向您页面的链接,但我建议仅更改查询字符串而不是完整的 URL。如果不使用此参数,则 URL 不会更改。

history.pushState(state, title[, location])

此函数的工作方式与history.replaceState 相同,不同之处在于它将新状态置于当前状态而不是替换当前状态。之前可以使用forward 访问的所有历史状态都将被丢弃,新状态将成为当前状态。

组装零件

历史界面非常有用,可让您的用户在浏览器中浏览动态生成的内容,而无需重新加载整个页面,但您需要注意您的用户可能会做的所有可能影响历史状态。

  1. 第一次导航到您的页面
    • 您的用户应该收到菜单/列表、一些特定的动态生成的内容,还是一些随机动态生成的内容?
    • 没有history,甚至没有JavaScript,您的页面是否能正确显示?
  2. 使用back/forward返回您的页面
    • 您的用户应该看到他们第一次看到的相同内容,还是应该看到内容中反映的访问结果? (“欢迎回来”消息可能对某些人来说是一种很好的接触,但对其他人来说却是一种不必要的分心。)
  3. 刷新页面
    • 您应该获得一个新页面、返回起始页面还是重新加载同一页面? (如果 URL 没有更改,您的用户可能不会期望最后一个。)
  4. 在刷新的页面中使用back/forward
    • 您应该获取与刷新页面相关的新内容,还是重新加载之前保存的状态?
  5. 离开您的页面
    • 您在离开前需要保存任何东西吗?
  6. 通过deep link 返回您的页面
    • 您是否有代码来识别和处理深层链接?

请注意,无法删除已保存的状态(除了上面提到的 pushState() 的特定情况)。您只能将其替换为新内容。

把它们放在一起

由于这开始有点罗嗦,让我们用一些代码来完成它。

// This function is called when the page is first loaded, when the page is refreshed,
// and when returning to the page from another page using back/forward.
// Navigating to a different page with history.pushState and then going back
// will not trigger this event as the page is not actually reloaded.
window.onload = function() {
  // You can distinguish a page load from a reload by checking performance.navigation.type.
  if (window.performance && window.PerformanceNavigation) {
    let type = performance.navigation.type;
    if (type == PerformanceNavigation.TYPE_NAVIGATE) {
      // The page was loaded.
    } else if (type == PerformanceNavigation.TYPE_RELOAD) {
      // The page was reloaded.
    } else if (type == PerformanceNavigation.TYPE_BACK_FORWARD) {
      // The page was navigated to by going back or forward,
      // though *not* from a history state you have set.
    }
  }

  // Remember that the browser automatically sets the state to null on the
  // first visit, so if you check for this and find it to be null, you know
  // that the user hasn't been here yet.
  if (history.state == null) {
    // Do stuff on first load.
  } else {
    // Do stuff on refresh or on returning to this page from another page
    // using back/forward. You may want to make the window.onpopstate function
    // below a named function, and just call that function here.
  }

  // You can of course have code execute in all three cases. It would go here.

  // You may also wish to set the history state at this time. This could go in the
  // if..else statement above if you only want to replace the state in certain
  // circumstances. One reason for setting the state right away would be if the user
  // navigates to your page via a deep link.
  let state = ...; // There might not be much to set at this point since the page was
                   // just loaded, but if your page gets random content, or time-
                   // dependent content, you may want to save something here so it can
                   // be retrieved again later.
  let title = ...; // Since this isn't actually used by your browser yet, you can put
                   // anything you want here, though I would recommend setting it to
                   // null or to document.title for when browsers start actually doing
                   // something with it.
  let URL = ...;   // You probably don't want to change the URL just yet since the page
                   // has only just been loaded, in which case you shouldn't use this
                   // variable. One reason you might want to change the URL is if the
                   // user navigated to this page with a query string in the URL. After
                   // reading the query string, you can remove it by setting this
                   // variable to: location.origin + location.pathname
  history.replaceState(state, title, URL); // Since the page has just been loaded, you
                                           // don't want to push a new state; you should
                                           // just replace the current state.
}

// This function is called when navigating between states that you have set.
// Since the purpose of `history` is to allow dynamic content changes without
// reloading the page (ie contacting the server), the code in this function
// should be fairly simple. Just things like replacing text content and images.
window.onpopstate = function() {
  // Do things with history.state here.
}

// This function is called right before the page is refreshed, and right
// before leaving the page (not counting history.replaceState). This is
// your last chance to set the page's history state before leaving.
window.onunload = function() {
  // Finalize the history state here.
}

请注意,我从未在任何地方打电话给history.pushState。这是因为history.pushState 不应在这些函数中的任何位置调用。它应该由实际更改页面的函数调用,您希望用户能够使用后退按钮撤消。

因此,总而言之,通用设置可能会像这样工作:

  • window.onload 函数中检查if (history.state == null)
    • 如果为真,则用新信息覆盖历史状态。
    • 如果为 false,则使用历史状态恢复页面。
  • 当用户浏览页面时,当重要的事情发生并且应该可以通过后退按钮撤消时,请致电history.pushState
  • 如果/当用户使用后退按钮并触发popstate 事件时,请使用您设置的历史状态将页面返回到之前的状态。
    • 如果/当用户使用他们的前进按钮时也这样做。
  • 使用unload 事件在用户离开页面之前完成历史状态。

【讨论】:

  • 请注意,unload 不是完成页面的唯一选项。还有beforunloadpagehidevisibilitychange。如果您需要使用这些事件之一,那么您应该仔细研究每个事件的工作原理,以便为您的用例选择最佳的事件。
猜你喜欢
  • 2015-11-18
  • 2016-03-12
  • 2015-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多