Saving Application State in History (II)
Posted on June 23, 2010Sergio Cinos Architecture Engineer
In the previous article, we discussed how to keep our application state saved in the History, so when the user goes back to a visited page, we can load and display the state the application was in.
In most browsers, you can use document.location.hash=”#your-state-encoded-as-string” and everything works ok. However, in Internet Explorer 6 and Interner Explorer 7 (IE) this doesn’t work. You need to use an IFRAME to save application state in their URL. As we previously saw, in IE, you need to perform a new request if you want to save state in history. You can ‘fake’ that request using the technique displayed in the previous article, but it doesn’t work if you are using ‘document.location’ to enable cross-domain access (i.e.: iframes from different subdomains).
When you use ‘document.domain’ in your page, IE enters in a secure-mode that forces you to set ‘document.domain’ in every document (i.e.: main document and every iframe) used in the page. Also, it puts document.location in read-only mode, so you can’t change any document URL from JavaScript (you can’t write in document.location), forcing you to make a real request to the proper URL.
As we stated in the previous article, if the new URL only differs in the hash, it isn’t saved in the History, which was our main goal. In order to be saved in the History, the pathName (the part after <domain>/ and before ‘?’) or the queryString (the part between ‘?’ and ‘#’) must be different from the last request performed. It may seem that we need an extra request for each application state we want to save. Even though we are using basic optimization (such as not saving the same state twice in a row), those extra requests may be unacceptable for your needs. It depends on your backend and server configuration, but usually changing the queryString is easier and faster that changing pathName.
There is a way to mitigate the extra requests problem, using browser cache and some tricks combining the query string and the hash. For the URL to be saved in History the query string of each request must be different from the last one, but it can be equal to other used urls, so we can use two query strings alternatively:
1. Redirection to ?0#myState1
2. Redirection to ?1#myState2
3. Redirection to ?0#myState3
4. …
This is a very good approach, because we only do two requests, no matter how many states we are saving. But there is a problem: if the state’s representation is equal to any other previous state’s representation and they use the same query string, the requests is not performed and the entry in History is not saved for that state. In other words, if we are loading to something like ‘?1#app=Index%user_id=47′ and we have loaded the same string in the past, this new URL is not saved in History. This collision can be very frequent, making our History system unreliable.
A good solution is to add a random number to each request, so each URL is different and IE saves it in History. However, it may be that you can’t do it if you are implementing this in an existing system (you might have thousands of code lines to change by hand), and you need to modify your system to discard the random number while restoring a saved state. So just in case, we present a solution to minimize the requests without messing around with the string representing the state.
The trick is that IE saves a URL in the History if queryString is different, but if it has cached a URL with the same queryString (and pathName, of course), it loads it from the cache avoiding the real request. We can use this to minizime the number of requests:
We have a sorted list of slots, called ’slot0′, ’slot1′, ’slot2′ and so on. Each slot has associated the application states that have been saved using this particular slot. Slots are created dynamically as needed. Each time we want to save a application state, we load the URL ‘?<slot>#<state-data>’ in the iframe, where:
<slot>: This is the first slot in the list that doesn’t have <state-data> associated yet and that is different from the previous used <slot>.
<state-data>: The string representing application state.
After doing the request, we associate <state-data> with the <slot> used. This is more clear with an example:
1. Saving state ‘myState1′: redirection to ‘?slot0#myState1′ with extra request, since it is the first time we use ’slot0′
2. Saving state ‘myState2′: redirection to ‘?slot1#myState2′ with extra request for ’slot1′ because it must be different from ’slot0′
3. Saving state ‘myState3′: redirection to ‘?slot0#myState3′ without extra request (back to ’slot0′, let the browser cache handle this request).
4. Saving state ‘myState4′: redirection to ‘?slot1#myState4′ without extra request
5. Saving state ‘myState3′ (again): redirection to ‘?slot2#myState3′ with extra request (we can’t use ’slot0′ again because it has myState3 already associated, and we can’t use ’slot1′ because we used it in the previous request).
This solution works for Internet Explorer 6 and Internet Explorer 7 and is fully compatible with ‘document.domain’. The only drawback is that you need to perform a few server requests.






July 27, 2010 at 4:28 pm
Ingenious solution! However I do have one thing to add: how much progress must we lose to concentrate on making every last feature compatible with outdated systems?
July 30, 2010 at 10:47 am
Hehe
Good question. TBH I don’t think we’re the ones to decide. It’s users.
Internally we have a set of rules that define if the browser/version is supported by Tuenti, also basing on the number of users still using that technology. Of course we can promote upgrading IE, but then not all people can and it would be a bit discriminating if we’d drop support for a relatively large users group.