Skip to content Skip to sidebar Skip to footer

Ajax With History.pushstate And Popstate - What Do I Do When Popstate State Property Is Null?

I'm trying out the HTML5 history API with ajax loading of content. I've got a bunch of test pages connected by relative links. I have this JS, which handles clicks on those links.

Solution 1:

This is an older question but there is a much simpler answer using native javascript for this issue.

For the initial state you should not be using history.pushState but rather history.replaceState.

All arguments are the same for both methods with the only difference is that pushState creates a NEW history record and thus is the source of your problem. replaceState only replaces the state of that history record and will behave as expected, that is go back to the initial starting page.

Solution 2:

I ran into the same issue as the original question. This line

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

should be changed to

var initialPop = !popped;

This is sufficient to catch the initial pop. Then you do not need to add the original page to the pushState. i.e. remove the following:

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

The final code based on AJAX tabs (and using Mootools):

if ( this.supports_history_api() ) {
    var popped = ('state'inwindow.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');
       }
   });
}

Solution 3:

I still don't understand why the back button behaves like this - I'd have thought the browser would be happy to step back to an entry that was created by a normal request. Maybe when you insert other entries with pushState the history stops behaving in the normal way. But I found a way to make my code work better. You can't always depend on the state property containing the URL you want to step back to. But stepping back through history changes the URL in the address bar as you would expect, so it may be more reliable to load your content based on window.location. Following this great example I've changed my popstate handler so it loads content based on the URL in the address bar instead of looking for a URL in the state property.

One thing you have to watch out for is that some browsers (like Chrome) fire a popstate event when you initially hit a page. When this happens you're liable to reload your initial page's content unnecessarily. So I've added some bits of code from the excellent pjax to ignore that initial 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 popstatevar popped = ('state'inwindow.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 loadvar initialPop = !popped && location.href == initialURL;
        popped = true;
        if (initialPop) return;

        console.log('Popstate');

        // By the time popstate has fired, location.pathname has been changedajaxLoadPage(location.pathname);

    });

});

One improvement you could make to this JS is only to attach the click event handler if the browser supports the history API.

Solution 4:

I actually found myself with a similar need today and found the code you provided to be very useful. I came to the same problem you did, and I believe all that you're missing is pushing your index file or home page to the history in the same manner that you are all subsequent pages.

Here is an example of what I did to resolve this (not sure if it's the RIGHT answer, but it's simple and it works!):

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

Hope this helps!

Solution 5:

I realize this is an old question, but when trying to manage state easily like this, it might be better to take the following approach:

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

in this way, you can specify an optional callback function on the state object when adding to history, then when popstate is trigger, this function would be called with the state object as a parameter.

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

You could easily extend this to suit your needs.

Post a Comment for "Ajax With History.pushstate And Popstate - What Do I Do When Popstate State Property Is Null?"