5

I used (https://github.com/browserstate/history.js) and have a piece of code like this

History.Adapter.bind(window, 'statechange', function() { 
    var State = History.getState();
    alert('Inside History.Adapter.bind: ' + State.data.myData);
});

function manageHistory(url, data, uniqueId){
    var History = window.History;
    if ( !History.enabled ) { return false; }        
    History.replaceState({myData: data}, null, '?stateHistory=' + uniqueId);
}

if I invoke manageHistory() after my ajax call, then History.Adapter.bind callback method get invoked correctly. However, if I click the browser back and then click forward button that result in page navigation from B to A, then A back to B, call back method inside History.Adapter.bind does not get invoked. This happen on both chrome and IE9. Anyone know how to fix this issue, I need to get the State after click browser back and then forward, to update my DOM. Please help

Note: I use version 1.7.1 jquery.history.js for html4 browser IE9

UPDATE (May 3 2013): Talk a bit more about my requirement

Sorry I was busy with other task, that not until now that I have sometimes to look at this issue. So oncomplete of an ajax call, I took that information that return to me, and push it into state. My requirement is: that if I navigate from page A to page B, and then on page B, I execute numbers of ajax requests that result in DOM manipulation (let call each ajax request that result in DOM manipulation a state). If I click the back button, I should go back to page A, and if I click the forward button, I should go to page B with the last state that I was in.

So my thought was, oncomplete of my ajax request, I would replace the last state in history with my current state (my json object is the html that I receive back from the ajax request). If I use push state, let say I am on page B, and I click one button, my page now change to aaa, I pushState aaa. Then if I click other button, my page now change to bbb, I pushState bbb. If I click the back button now, I would still be on page B, with my state in History.Adapter.bind(window, 'statechange', function() {}) show as aaa, if I click back button again, i would go to page A. I do not want this behavior. I want that if I am on page B with state bbb, and click back, I would go to page A, and if I click forward, I would go to page B with state bbb. I hope this make more sense. Please help

alex
  • 479,566
  • 201
  • 878
  • 984
Thang Pham
  • 38,125
  • 75
  • 201
  • 285
  • re: your update - I think you do not want to use history to track "state changes" within page B/A. only use history in conjunction with a change in your URL. Any state changes within a single url (ie A vs B) should save state by another mechanism (not by history.replaceState(); ... This is very easy to do with an MV* Library like Backbone or Angular. – 1nfiniti May 06 '13 at 17:03
  • Check out jquery-mobile, they have an ajaxy way of handling pages that you might find useful: http://jquerymobile.com/ – euxneks May 08 '13 at 22:12

3 Answers3

5

What you need to do is as follows:

  • If the page you came from is another page (e.g. you open page B and the previous page was page A) you do a pushState.
  • If on the other hand the previous page was the same page as you're currently on you will simply do a replaceState, replacing the information with information about your current state.

(the tracking of the previous page you will need to do manually in some variable)

This means that if you go from A to B (pushState) to new state of B (replaceState), back button (goes to state info of A) and next forward (goes to the newest state of B). Additionally if you open A the first time you need to do a replaceState with information about the state of A so that A will have some state at least (otherwise it will have an empty data object).

It might be useful to look at this answer of mine, which is simply about the way you would built an ordered history stack with pushStates. Not exactly what you want of course, but it's a bit simpler written and together with the information in this answer it should get you the behaviour you want.

Community
  • 1
  • 1
David Mulder
  • 26,123
  • 9
  • 51
  • 114
3

I see that you are using History.replaceState which remove the last history state in the stack and replace it by the state given in parameters.

I am using History.pushState in my website, and doesn't face such an issue because this function doesnt pull the last state but add the new state above it. It is making the back-forward buttons work correcly.

I hope it helps you.

Edit: Using the example of change of select tag as event listenner:

function manageHistory(url, data, uniqueId){
    var History = window.History;
    if ( !History.enabled ) { return false; }        
    History.replaceState({myData: data}, null, '?stateHistory=' + uniqueId);
}

$('select.select').change(function(e) {

    e.preventDefault(); 

    // get url
    // get data
    // get uniqueId

    manageHistory(url, data, uniqueId)


});

History.Adapter.bind(window, 'statechange', function() { 
    var State = History.getState();
    // Launch the ajax call and update DOM using State.data.myData
});
Adib Aroui
  • 4,981
  • 5
  • 42
  • 94
  • the thing is, with my requirement, I cannot use pushState. If I use pushState, back button will get back to every ajax request, but my requirement is a bit different, I only want to save the last state – Thang Pham Apr 23 '13 at 15:02
  • I think we dont't have the same understanding of history.js. What I know is that browser buttons will get back to the ajax request if you History.Adapter.bind ajax request with button click. If you only want to save the last state, just use pushState and bind nothing.Sorry I am really new to this. – Adib Aroui Apr 23 '13 at 15:25
  • in addition I see that you say:if I invoke manageHistory() after my ajax call... I think you should execute your ajax call inside managehistory after pushing the state in stack – Adib Aroui Apr 23 '13 at 15:28
  • 1
    @ThangPham you can use pushState , it wont get back any ajax request unless u explicitly made the ajax call based on the data you saved in State... Can you please explain your requirement further? – NavaRajan Apr 24 '13 at 16:18
  • @NavaRajan: Sorry I was busy with other task, that not until now that I have sometimes to look at this issue. I have update my requirement in my original question. Please have a look. `whiteletters and blankspaces`: I also explain why I use replaceState instead of pushState, please see my update in my original question – Thang Pham May 03 '13 at 09:14
  • @ThangPham I have seen your edit, but I feel we are lacking some additionnal code. Please to provide with an overview of the AJAX code and its positionning related to managehistory(). Thanks. – Adib Aroui May 03 '13 at 18:57
  • @whitelettersandblankspaces: The ajax code is web framework specific, so that why I did not post it here. But the idea AJAX communicate with server for some business logic, and update the DOM, `oncomplete` of the AJAX call, I call `manageHistory()` method – Thang Pham May 04 '13 at 04:28
  • Let me please repeat my approach when I use history.js, an approach that works correctly for me. I would do in your case: call manageHistory inside the event listener (the click or the change or ...) not oncomplete of the ajax call, and launch the ajax call inside History.Adapter.bind separately. please to see it in my edit – Adib Aroui May 04 '13 at 06:53
2

Also,

relpaceState will always erase B state.

Use pushState for A and B states.

And use replacestate for aaa, bbb, ccc states.

function manageHistoryBack(url, data, uniqueId){
    var History = window.History;
    if ( !History.enabled ) { return false; }        
    History.replaceState({myData: data}, null, '?stateHistory=' + uniqueId);


}

function manageHistory(url, data, uniqueId){
    var History = window.History;
    if ( !History.enabled ) { return false; }        
    History.pushState({myData: data}, null, '?stateHistory=' + uniqueId);


}

// Event listenner triggering aaa,bbb,ccc 
$('select.select').change(function(e) {

    e.preventDefault(); 

    // get url
    // get data
    // get uniqueId

    manageHistoryBack(url, data, uniqueId)


});

// Event listenner triggering A, B
$('select.select').change(function(e) {

    e.preventDefault(); 

    // get url
    // get data
    // get uniqueId

    manageHistory(url, data, uniqueId)


});


History.Adapter.bind(window, 'statechange', function() { 
    var State = History.getState();
    // Launch the ajax call and update DOM using State.data.myData 
});

Good luck

Adib Aroui
  • 4,981
  • 5
  • 42
  • 94