带有history.pushState和popstate的Ajax – 当popstate state属性为null时,我该怎么办?

我正在尝试使用ajax加载内容的HTML5历史API。

我有一堆通过相关链接连接的测试页面。 我有这个JS,它处理这些链接的点击。 单击链接时,处理程序会获取其href属性并将其传递给ajaxLoadPage(),该页面将所请求页面中的内容加载到当前页面的内容区域中。 (我的PHP页面设置为在正常请求时返回完整的HTML页面,但只有一小部分内容,如果?fragment = true附加到请求的URL。)

然后我的click处理程序调用history.pushState()以在地址栏中显示URL并将其添加到浏览器历史记录中。

$(document).ready(function(){ var content = $('#content'); var ajaxLoadPage = function (url) { console.log('Loading ' + url + ' fragment'); content.load(url + '?fragment=true'); } // Handle click event of all links with href not starting with http, https or # $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){ e.preventDefault(); var href = $(this).attr('href'); ajaxLoadPage(href); history.pushState({page:href}, null, href); }); // This mostly works - only problem is when popstate happens and state is null // eg when we try to go back to the initial page we loaded normally $(window).bind('popstate', function(event){ console.log('Popstate'); var state = event.originalEvent.state; console.log(state); if (state !== null) { if (state.page !== undefined) { ajaxLoadPage(state.page); } } }); }); 

使用pushState向历史记录添加URL时,还需要为popstate事件包含一个事件处理程序,以处理后退或前进按钮上的点击。 (如果你不这样做,点击后面会显示你在地址栏中推送到历史记录的URL,但页面没有更新。)所以我的popstate处理程序抓取保存在我创建的每个条目的state属性中的URL,并将其传递给ajaxLoadPage以加载适当的内容。

这适用于我的点击处理程序添加到历史记录的页面。 但是,当我“正常”请求浏览器时,浏览器添加到历史记录的页面会发生什么? 假设我正常登陆我的第一页,然后通过点击进行ajax加载浏览我的网站 – 如果我然后尝试返回历史记录到第一页,最后一次点击显示第一页的URL,但不是不要在浏览器中加载页面。 这是为什么?

我可以看到这与最后一个popstate事件的state属性有关。 对于该事件,state属性为null,因为它只是由pushState()或replaceState()添加到历史记录中的条目,可以为其赋值。 但我第一次加载页面是一个“正常”的请求 – 为什么浏览器不会退回并正常加载初始URL?

这是一个较旧的问题,但使用本机javascript解决此问题的答案要简单得多。

对于初始状态,您不应使用history.pushState ,而应使用history.replaceState

两种方法的所有参数都是相同的,唯一的区别是pushState创建了一个新的历史记录,因此是您的问题的根源。 replaceState仅替换该历史记录的状态,并将按预期运行,即返回初始起始页。

我遇到了与原始问题相同的问题。 这条线

 var initialPop = !popped && location.href == initialURL; 

应该改为

 var initialPop = !popped; 

这足以捕捉最初的流行音乐。 然后您不需要将原始页面添加到pushState 。 即删除以下内容:

 var home = 'index.html'; history.pushState({page:home}, null, home); 

最终代码基于AJAX选项卡(并使用Mootools):

 if ( this.supports_history_api() ) { var popped = ('state' in window.history && window.history.state !== null) , changeTabBack = false; window.addEvent('myShowTabEvent', function ( url ) { if ( url && !changingTabBack ) setLocation(url); else changingTabBack = false; //Make sure you do not add to the pushState after clicking the back button }); window.addEventListener("popstate", function(e) { var initialPop = !popped; popped = true; if ( initialPop ) return; var tabLink = $$('a[href="' + location.pathname + '"][data-toggle*=tab]')[0]; if ( tabLink ) { changingTabBack = true; tabLink.tab('show'); } }); } 

我仍然不明白为什么后退按钮的行为是这样的 – 我以为浏览器会很高兴回到正常请求创建的条目。 也许当您使用pushState插入其他条目时,历史记录将以正常方式停止运行。 但我发现了一种让我的代码更好的方法。 您不能始终依赖包含要返回的URL的state属性。 但是,回过头历史可以像预期的那样更改地址栏中的URL,因此基于window.location加载内容可能更可靠。 按照这个很好的例子,我改变了我的popstate处理程序,因此它根据地址栏中的URL加载内容,而不是在state属性中查找URL。

您需要注意的一件事是,当您最初点击某个页面时,某些浏览器(如Chrome)会触发一个popstate事件。 发生这种情况时,您可能会不必要地重新加载初始页面的内容。 所以我从优秀的pjax中添加了一些代码来忽略那个初始的pop。

 $(document).ready(function(){ // Used to detect initial (useless) popstate. // If history.state exists, pushState() has created the current entry so we can // assume browser isn't going to fire initial popstate var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href; var content = $('#content'); var ajaxLoadPage = function (url) { console.log('Loading ' + url + ' fragment'); content.load(url + '?fragment=true'); } // Handle click event of all links with href not starting with http, https or # $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){ e.preventDefault(); var href = $(this).attr('href'); ajaxLoadPage(href); history.pushState({page:href}, null, href); }); $(window).bind('popstate', function(event){ // Ignore inital popstate that some browsers fire on page load var initialPop = !popped && location.href == initialURL; popped = true; if (initialPop) return; console.log('Popstate'); // By the time popstate has fired, location.pathname has been changed ajaxLoadPage(location.pathname); }); }); 

您可以对此JS进行的一项改进只是在浏览器支持历史记录API时附加click事件处理程序。

我实际上发现自己今天有类似的需求,并发现你提供的代码非常有用。 我遇到了你所遇到的同样问题,我相信你所缺少的就是将你的索引文件或主页推送到历史记录中,就像你所有后续页面一样。

这是我为解决这个问题所做的一个例子(不确定它是否是正确答案,但它很简单且有效!):

 var home = 'index.html'; history.pushState({page:home}, null, home); 

希望这可以帮助!

我意识到这是一个老问题,但是当试图像这样轻松地管理状态时,采取以下方法可能更好:

 $(window).on('popstate',function(e){ var state = e.originalEvent.state; if(state != null){ if(state.hasOwnProperty('window')){ //callback on window window[state.window].call(window,state); } } }); 

通过这种方式,您可以在添加到历史记录时在状态对象上指定可选的回调函数,然后在触发popstate时,将使用状态对象作为参数调用此函数。

 function pushState(title,url,callback) { var state = { Url : url, Title : title, }; if(window[callback] && typeof window[callback] === 'function') { state.callback = callback; } history.pushState(state,state.Title,state.Url); } 

您可以轻松扩展它以满足您的需求。

最后说:

我原以为浏览器很乐意回到正常请求创建的条目。

我在这里找到了一个奇怪的浏览器行为的解释。 解释是

您应该在第一次加载网站时保存状态 ,之后每次更改状态时保存状态

我测试了这个 – 它有效。

这意味着无需基于window.location加载您的内容

我希望我不会误导。