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

And*_*lly 21 javascript jquery html5 browser-history html5-history

我正在尝试使用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
    // e.g. 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);
            }
        }
    });

});
Run Code Online (Sandbox Code Playgroud)

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

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

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

小智 13

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

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

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


Eri*_*rin 6

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

var initialPop = !popped && location.href == initialURL;
Run Code Online (Sandbox Code Playgroud)

应改为

var initialPop = !popped;
Run Code Online (Sandbox Code Playgroud)

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

var home = 'index.html';
history.pushState({page:home}, null, home);
Run Code Online (Sandbox Code Playgroud)

最终代码基于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');
       }
   });
}
Run Code Online (Sandbox Code Playgroud)


And*_*lly 5

我仍然不明白为什么后退按钮的行为是这样的 - 我以为浏览器会很高兴回到由普通请求创建的条目.也许当您使用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);

    });

});
Run Code Online (Sandbox Code Playgroud)

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