It seems that nobody cares much for web2py, ajax and browser back buttons 
:) Nevertheless, I made some progress and thought I'd post some findings 
here If anyone searches the group with a similar goal.

Let me describe the problem once again. Imagine a scenario:

1) visitor is on Google, searches for a web2py powered site, finds it, 
follows the link
2) site loads
3) visitor sees an internal link which loads a web2py component (ajax call) 
with new content, follows the link, component loads
4) visitor reads the content, wants to go back, uses the browser back 
button...
5) KABOOM, user is back on Google instead on the first page of the site

which is pretty bad as far as user experience goes.

Anyway, our site has a number of components that can work as regular pages 
(.load and .html do the same thing) so what I really need is a way for a 
component to tell the browser it has loaded. To solve this, i need to 
manipulate the browser history manually.

Sadly, current state of things is pretty messy on the browser side. After 
looking at what's available, I found several solutions but none of them 
perfect. I decided to give up on Internet Explorer 8 and 9 completely. 
Sorry folks, you're on your own. IE 10 *should* work as it comes out... 
yes, I believe in Santa. :)

So, the first thing I tried was using the native browser's history API [1], 
which all modern HTML5 browsers have (yes, forget IE9). It looks fine on 
paper, but the implementation has some problems. Chromium / Chrome behaves 
differently than Firefox/Opera/Safari in one crucial point (fires initial 
onpopstate, I even managed to crash Chromium) [2], so I had to abandon 
using this API directly. If it wasn't for this Chromium issue, it would be 
the perfect solution.

Then I tried using the history.js [3]. This project has a long history of 
successfully coping with ajax/browser history issues, so I was optimistic. 
But sadly, history.js's master branch (version 1.7.1) also works in a way 
that pushState and replaceState will immediately trigger onpopstate, which 
is not what I wanted [4]. Luckily, the devel branch (1.8.0-dev) got an 
additional State.internal [5] with this commit [6], so I can tell if 
onpopstate is triggered by the browser's buttons or not.

Ok, long story is coming to an end. It's time for some code. This is what 
I'm currently at:

- set the history entry in the component's controller:
response.js = 
"History.pushState({isMyPageName:1},'','"+URL('some','thing',extension=False)+"')"

-at the end of layout.html:
<script>
       (function(window,undefined){
            var History = window.History;
            if ( !History.enabled ) {
                return false;
                }
            History.Adapter.bind(window,'statechange',function(){
                var State = History.getState();
                if (( State.data.isMyPageName == 1 ) && ( State.internal == 
false )) {
                    //alert("data: " + JSON.stringify(State.data) + " 
title: " + State.title + " url: " + State.url + " internal: " + 
State.internal);
                    window.location = State.url;
                    }
                });
            })(window);
</script>

Ok, that's about it... The solution is flaky at times and I still need to 
manipulate document.title, haven't gotten to that yet. But it works, web2py 
components can silently change the browser url as they load. And no &#&%/ 
URL hashes and hashbangs...

[1] https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
[2] 
https://github.com/balupton/history.js/wiki/The-State-of-the-HTML5-History-API
[3] https://github.com/balupton/history.js
[4] https://github.com/balupton/history.js/issues/47
[5] https://github.com/balupton/history.js/tree/dev
[6] 
https://github.com/balupton/history.js/commit/c79c17f9801373f5218f144088c387f1935f3462

Reply via email to