Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ajax with history.pushState and popstate - what do I do when popstate state property is null?

Tags:

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. When a link is clicked the handler grabs its href attribute and passes it to ajaxLoadPage(), which loads content from the requested page into the content area of the current page. (My PHP pages are set up to return a full HTML page if you request them normally, but only a chunk of content if ?fragment=true is appended to the URL of the request.)

Then my click handler calls history.pushState() to display the URL in the address bar and add it to the browser history.

$(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);             }         }     });  }); 

When you add URLs to the history with pushState you also need to include an event handler for the popstate event to deal with clicks on the back or forward buttons. (If you don't do this, clicking back shows the URL you pushed to history in the address bar, but the page isn't updated.) So my popstate handler grabs the URL saved in the state property of each entry I created, and passes it to ajaxLoadPage to load the appropriate content.

This works OK for pages my click handler added to the history. But what happens with pages the browser added to history when I requested them "normally"? Say I land on my first page normally and then navigate through my site with clicks that do that ajax loading - if I then try to go back through the history to that first page, the last click shows the URL for the first page, but doesn't load the page in the browser. Why is that?

I can sort of see this has something to do with the state property of that last popstate event. The state property is null for that event, because it's only entries added to the history by pushState() or replaceState() that can give it a value. But my first loading of the page was a "normal" request - how come the browser doesn't just step back and load the initial URL normally?

like image 730
And Finally Avatar asked Jan 08 '13 12:01

And Finally


People also ask

Does history back trigger Popstate?

The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling history. back() or history. forward() in JavaScript). Browsers tend to handle the popstate event differently on page load.

Does pushState trigger Popstate?

html - history. pushState does not trigger 'popstate' event - Stack Overflow. Stack Overflow for Teams – Start collaborating and sharing organizational knowledge.

What is the use of history pushState?

In an HTML document, the history. pushState() method adds an entry to the browser's session history stack.

Does history pushState reload page?

The history. pushState() method can be used to push a new entry into the browser's history—and as a result, update the displayed URL—without refreshing the page. It accepts three arguments: state , an object with some details about the URL or entry in the browser's history.


2 Answers

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.

like image 197
Matt Avatar answered Sep 23 '22 12:09

Matt


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' 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');        }    }); } 
like image 26
Erin Avatar answered Sep 22 '22 12:09

Erin