How to mix AJAXy page updates using the back button so that updates are still present when the user returns?

I have a page that the user can change. All modifications are performed using jQuery and are also sent to the server, so a full reload will also lead to a page change.

This works fine in Firefox 11 / Chrome on Windows: even if the user moves to another location and then uses the back button, they get a page with the latest changes.

However, if I now embed Google Maps on the page, the Back button stops working: it displays the user on the page, as it was before all their changes. This page no longer exists except the browser cache, and yet it is displayed.

Ive put together a simple test folder here that shows this behavior.

What gives? How can i fix this? An ideal solution will allow the browser to return without reloading the page, as usual.

PS Apparently, the “working” example doesn’t really work in Chrome on OSX. How can I get around the browser by insisting on reverting to an outdated version of the page?

Bug reports describing this behavior: Firefox


Bounty : Firefox and Chrome on Windows demonstrate both behaviors (returning to a modified DOM in one case, but not changed in another). Is there a specification describing what a browser should do? Are there any errors to change this anyway? Does this problem have a common name that I can make Google?

I consider a solution in which I update a hidden element using JavaScript, and then check if the update is updated. If so, the back button has restored the updated DOM, and nothing else needs to be done. If not, the browser has restored the outdated DOM, and I can just force the page to reload, as it is unpleasant. Any comments on this approach are also welcome.

Note : on a real website there are more editable controls, and one of them is a free-form text area. I would like the proposed solutions to work even if the user has just added a few paragraphs of text. For example, this type cannot be added to the URL after # .

+7
source share
7 answers

Inclusion of Google Maps on the page disables bfcache (I will use the term mozilla due to the lack of a standard one), because the unload listener unload used on the map page loaded in the <iframe> .

Possible reasons for not caching a page for fast rewinding in Firefox are indicated in MDN: Using Firefox 1.5 caching . Your problem is listed as “a top-level page contains frames that are not cached”, which is confusing, I will try to clarify it later. (Other browsers probably use similar heuristics, as these rules were designed to not violate existing content - see also this answer , it has some links.)

The right way to fix this is to make friends with someone on Google and then warm them until they remove the onunload , at least from the built-in map pages.

In general, you should never rely on bfcache to work or not work on a specific page. This is just an optimization for the general case. Since this is an optimization, it can be disabled, for example, when the system remains low in memory. It will also not work if the user restarts the browser before returning or closes the tab and selects “cancel closing the tab”, as you noted in the error.

You must either restore the state of the page from JS, or mark the page as cacheable (using the HTTP header). The first, of course, leads to a better user experience. The @Adam Gent suggestion looks right, I will need to check which Firefox problem it is addressing.


The reason bfcache works as follows:

  • If the browser launches the onunload handler and then restores the page via bfcache, the page may be damaged, because scripts often delete event listeners in the onunload handler (in order to “clear” what is not really necessary except the old IE version)
  • if browsers have stopped using onunload handlers on the page, based on the fact that the user can return to the page and they want to cache it, the authors will complain.
  • If the page in the iframe cannot be cached, restoring the external cached page and reloading the internal page sometimes interrupts it (for example, if both pages have the same domain, the external page may contain links to objects in the frame that will not be valid after the internal frame is reloaded). Therefore, if the iframe is not cached, none of them are parent.

The reason the page is still loading from the cache (disk) when you press back is because you allegedly indicated that the content sent to the browser could be cached. The browser does not know that you are updating the page on the server in parallel with the DOM changes.

Hope this helps.


[edit] I will talk about the idea of ​​"mark the page as not cached" above. To make the web browser cache work, not against you, it is important to remember that HTTP is a protocol for retrieving resources. For example, the HTML page identified by the URL http://bbb.akshell.com/broken is a resource. When you serve a resource through HTTP, you specify how long a copy of the browser resource will be valid (i.e., it matches the canonical version of the resource on the server).

When, as in your test case, the resource is an HTML page with an element selected by the user, marked in a special way, the resource can change at any time (every time the user changes the choice). This means that an honest HTTP response when serving this resource will be "not cached, may change at any time." Then the browser will reload the page from the server every time it needs to load the page - the correct behavior, but due to the slowness for the user.

An alternative approach, suitable for things like switching between multiple tabs on a page, is to associate each selection with its own URL. A page with two tabs will correspond to two resources (and two URLs) - one for each tab. Both resources can be (HTTP) cached by the browser. Changing the URL and page content can be implemented without accessing the server via pushState.

Another approach that seems more applicable to your case, in which you save user input on the server: separate the application user interface and user data from different resources (for example, a static (HTTP) cached HTML page with JS, loading user data from a separate non-cacheable URLs). Request user data and update the user interface at boot. [/ Edit]

+5
source

If you don't like older browsers, you can use a combination of local storage and pushstate.

Using the solution @ lucian.pantelimon + pushstate + localstorage, you get what you want.

 <script> window.addEventListener("popstate", function(e) { alert("hello"); // do my restoration from localstorage. }); </script> 

@TimWi is correct that if the browser does not fire any event, your type is up shit creek.

One thing you might want to explore is the really rude hacking of a frame set (ala reddit / google images / linkedin style).

EDIT: It seems that pushstate events do not fire when you click the back button on a remote site to your site.

EDIT 2: it looks like my firefox v 10.0 has problems with pushstate. It works great in chrome.

+1
source

This trick will clear bfcache for FireFox:

  window.onunload = function(){} 
+1
source

Your work page does not work for me - at least not the way you want it.

It looks like you are updating the DOM page through jQuery and then sending the update to the server, but you never make the browser grab the latest data from the server. Thus, the behavior of the Back button is completely dependent on the browser, which decides to either cache the last server request or the last state in which the DOM remains.

You can try updating the URI using the query string when you are updating the DOM through jQuery. This may cause the browser to capture the last upon return.

0
source

Assuming that you do not trust the browser, depending on the complexity of the changes, you can use the link name to indicate the status of the page.

For example, your "Choose me!" link # 3 will lead to http://bbb.akshell.com/broken#link3 . This will not reload the page. Thus, even if the page loads from scratch after clicking "Back", you still have the link # link3 in the url, and you can use it to get the page in the state you need (in this case, shoot "Choose me!". # 3).

You can also create several more complex URLs to offset more complex changes. I believe that such mechanics are somehow used to navigate a specific section of a flash site, when everything is processed by the same flash movie.

PS This is the solution "I do not believe the browser." If you trust - a solution that should work in each of them, I would be interested to see it as well.

0
source

You can try to store as much information as possible in cookies and "rearrange" the page when necessary.

This is not a completely clean approach, as cookies will be sent every time you access the page (limiting the amount of data stored in cookies is mandatory, I think), but it should be compatible with several browsers.

Another way is to save the data in window.name . For pros, cons, and alternatives for this approach, see question 203075 .

EDIT: It might be useful to save the state of the application and associate an identifier with each, and then add something like #stateID<state id here> to the address. When the user clicks the back button, you can find out what state to load.

IE:

Say your state contains selectMeItemSelected and mapInfo{latitude, longitude, zoomLevel} .

When the user first enters the site, you save stateID # 1 in cookies (or elsewhere, but I will use cookies in this example) as:

  state[1].selectMeItemSelected = 1; state[1].mapInfo.latitude = 45; state[1].mapInfo.longitude = 45; state[1].mapInfo.zoomLevel = 2; 

After that, you will add #stateID1 to the website address.

When a user takes an action that activates your AJAX code, you save a different state in cookies, change the URL to #stateID2 , and then resume execution of the AJAX code.

When you find that the user has pressed the back button (or forward), simply load the page using the information stored in the state.

If you create the stateID sequentially, you must also clear the states that follow the current state before adding a new state. This way you get the behavior of the "Back" and "Forward" buttons in the browser: if you click " stateX ", click another link (maybe AJAX), you will not have an active active direct access button to go back to stateX , and You may consider clearing this condition.

This approach does not work with bookmarks if you do not save state on the server and do not transmit status information every time or do not follow any permanent link.

I hope I managed to express my idea, because it is a little unclear when I re-read it. Just leave a comment if you have a question (if you find this information useful or a course).

Hope this helps :)

0
source

I don’t know if all browsers will trigger the domready event (or even any event) when you click the back button and go to the cache page. It is clear that if the browser does not trigger any events, then you can not do anything.

However, my suspicion is that they will fire the domready event. If they did not, the jQuery event hooks added via $(function() { ... }) would not be set, and if that were the case, I would probably encounter a lot more error (because it it would be much more noticeable to me as a user).

Therefore, my suggestion is to handle the windowunload † event; in this case, connect some code to the domready event, which checks whether the page is in an altered state or in its original unmodified state; and if it is in an unmodified state, force the page to reload. (Theoretically, you could reload this game so that it does not affect your browsing history.)

Therefore, you need to mark the page as modified or unmodified. For example, you can add an attribute to the body element every time the page changes. In jQuery, I would recommend using data :

 $(document.body).data('modified', true); 

Of course, the disadvantage is that you need to do this in every part of the ajaxy code that modifies the page. If you have a centralized function that all your ajaxy DOM modification performs, it's a little easier.

Here is my attempt at the windowunload † handler:

 $(window).unload†(function() { $(function() { if (!$(document.body).data('modified')) forceReload(); }); }); 

Of course, you have to write forceReload() yourself. It could be a simple call to location.reload‡() or ajaxy.

† I do not remember the exact name of the event or the jQuery function to connect to it.

‡ Similarly, check if it was caused by reload or refresh or something else.

0
source

All Articles