如何区分"真实"和"虚拟"的流行状态事件



我正在构建一个在非javascript回退之上使用AJAX和pushState/replaceState的工具(http://biologos.org/resources/find)。基本上,它是一个返回真实HTML链接列表的搜索工具(单击链接会使您退出该工具)。

我使用的是onpopstate,因此用户可以浏览由pushState创建的查询历史记录。当从真实链接(一个NOT使用pushState创建但由实际浏览器导航创建的链接)导航返回时,此事件也会触发。我不希望它在这里着火。

所以我的问题是:如何区分来自pushState历史项目的onpopstate事件和来自真实导航的事件?

我想做这样的事情:

window.onpopstate = function(event){
if(event.realClick) return;
// otherwise do something
}

我尝试过onoptstate处理程序-ajax后退按钮,但没有成功:(

提前感谢!

EDIT:这里的一个问题是不同浏览器处理onpopstate事件的方式。以下是似乎正在发生的事情:

  • 真实虚拟事件上激发onpopstate
  • 实际上重新运行javascript(所以设置loaded=false实际上会测试为false)
  • 上面链接中的解决方案确实有效

Firefox

  • 仅在虚拟事件上激发onpopstate
  • 实际上重新运行javascript(所以设置loaded=false实际上会测试为false)
  • 为了使链接的解决方案真正工作,loaded需要在页面加载时设置为true,这会破坏Chrome

Safari

  • 真实虚拟事件上激发onpopstate
  • 似乎NOT在事件之前重新运行javascript(因此,如果之前设置为true,loaded将为true!)

希望我只是错过了什么。。。

您可能可以使用history.js。它应该为您提供一个在所有主要平台上表现一致的API(尽管它可能无法解决这个特定问题;您必须尝试它来找出答案)。

然而,在我看来,处理这一问题(以及其他相关问题)的最佳方法是以这样一种方式设计应用程序,即这些问题无关紧要。自己跟踪应用程序的状态,而不是完全依赖于历史堆栈中的状态对象。

跟踪应用程序当前显示的页面。在与window.location分离的变量中跟踪它。当导航事件(包括popstate)到达时,将您已知的current page与请求的next page进行比较。首先要弄清楚是否真的需要更改页面。如果是这样,则呈现请求的页面,并在必要时调用pushState(仅对"正常"导航调用pushState,而从不响应popstate事件)。

处理popstate的代码也应该处理您的正常导航。就您的应用程序而言,应该没有什么区别(除了正常的导航包括对pushState的调用,而popstate驱动的导航没有)。

以下是代码中的基本思想(请参阅jsBin上的实时示例)

// keep track of the current page.
var currentPage = null;
// This function will be called every time a navigation
// is requested, whether the navigation request is due to
// back/forward button, or whether it comes from calling
// the `goTo` function in response to a user's click... 
// either way, this function will be called. 
// 
// The argument `pathToShow` will indicate the pathname of
// the page that is being requested. The var `currentPage` 
// will contain the pathname of the currently visible page.
// `currentPage` will be `null` if we're coming in from 
// some other site.
// 
// Don't call `_renderPage(path)` directly.  Instead call
// `goTo(path)` (eg. in response to the user clicking a link
// in your app).
//
function _renderPage(pathToShow) {
if (currentPage === pathToShow) {
// if we're already on the proper page, then do nothing.
// return false to indicate that no actual navigation 
// happened.
//
return false;
}
// ...
// your data fetching and page-rendering 
// logic goes here
// ...
console.log("renderPage");
console.log("  prev page  : " + currentPage);
console.log("  next page  : " + pathToShow);
// be sure to update `currentPage`
currentPage = pathToShow;
// return true to indicate that a real navigation
// happened, and should be stored in history stack
// (eg. via pushState - see `function goTo()` below).
return true;
}
// listen for popstate events, so we can handle 
// fwd/back buttons...
//
window.addEventListener('popstate', function(evt) {
// ask the app to show the requested page
// this will be a no-op if we're already on the
// proper page.
_renderPage(window.location.pathname);
});
// call this function directly whenever you want to perform
// a navigation (eg. when the user clicks a link or button).
// 
function goTo(path) {
// turn `path` into an absolute path, so it will compare
// with `window.location.pathname`. (you probably want
// something a bit more robust here... but this is just 
// an example).
//
var basePath, absPath;
if (path[0] === '/') {
absPath = path;
} else {
basePath = window.location.pathname.split('/');
basePath.pop();
basePath = basePath.join('/');    
absPath = basePath + '/' + path;
}
// now show that page, and push it onto the history stack.
var changedPages = _renderPage(absPath);
if (changedPages) {
// if renderPage says that a navigation happened, then
// store it on the history stack, so the back/fwd buttons
// will work.
history.pushState({}, document.title, absPath);
}
}
// whenever the javascript is executed (or "re-executed"), 
// just render whatever page is indicated in the browser's 
// address-bar at this time.
//
_renderPage(window.location.pathname);

如果您查看jsBin上的示例,您会发现每当应用程序请求转换到新页面时都会调用_renderPage函数——无论是由于popstate(例如back/fwd按钮),还是由于调用goTo(page)(例如某种用户操作)。它甚至在页面首次加载时被调用。

_renderPage函数中,您的逻辑可以使用currentPage的值来确定"请求来自哪里"。如果我们来自外部站点,那么currentPage将是null,否则,它将包含当前可见页面的路径名。

最新更新