Content with Style

Web Technique

Fixing the Back Button and Enabling Bookmarking for AJAX Apps

by Mike Stenhouse on June 15 2005, 09:01

The problem

Everyone's favourite AJAX technology app is Google Maps. Google have done a stunning job... But when I came to try to bookmark a page I had to hunt around for 'link to this page' over on the right hand side. Why have they broken such a basic function of the web? I use bookmarks A LOT and the extra effort bothered me. I got over it though, and life went on.

Then I came to flick through the drop down on Kottke.org (now removed) and I kept hitting the back button on my mouse by accident, taking me off the site. Really irritating. The most fundamental online behaviour - click then back, is broken.

I've not picked on these sites for any particular reason. They both happen to be great sites that I visit regularly enough to notice these flaws, which will be common to many AJAX-based applications.

After a chat with Jeremy Keith, Rich Rutter and Andy Budd about exactly this problem I decided to take a shot at fixing it.

Read on for the explanation or go straight to the demo to see it in action.

Standing on the shoulders of giants

I'm not the first person to tackle this type of problem. I've drawn inspiration and know-how from several places to get this up and running:

The original bookmark/back button fix, as used by Flash developers for a little while now:
www.robertpenner.com/experiments/backbutton/flashpage.html
I've not actually looked at how they implemented their solution but this is where I got the idea for replacing Robert Penner's frames with iframes:
dojotoolkit.org/intro_to_dojo_io.html#so-about-that-thorny-back-button
Rich Rutter's use of the hash for bookmarking:
www.clagnut.com/sandbox/slideshow.html#5
For this little experiment I've used Harry Fuecks' JPSpan
It's a fantastic framework that makes the methods you define in your server-side PHP classes available to your Javascript via XmlHttpRequest. It's the simplest way I've come across to get started with AJAX. I had the guts of my demo up and running in about 10 minutes!
I'm using Algorithm's Timer object:
www.codingforums.com/archive/index.php/t-10531.html
And Scott Andrew's cross-browser event handler:
www.scottandrew.com/weblog/articles/cbs-events

Setting things up

I created a PageLocator object to act as an interface to both real querystrings and my hash pseudo-querystrings. It's nothing complicated but it allows me to access both using the same methods...

function PageLocator(propertyToUse, dividingCharacter) {
    this.propertyToUse = propertyToUse;
    this.defaultQS = 1;
    this.dividingCharacter = dividingCharacter;
}
PageLocator.prototype.getLocation = function() {
    return eval(this.propertyToUse);
}
PageLocator.prototype.getHash = function() {
    var url = this.getLocation();
    if(url.indexOf(this.dividingCharacter) > -1) {
        var url_elements = url.split(this.dividingCharacter);
        return url_elements[url_elements.length-1];
    } else {
        return this.defaultQS;
    }
}
PageLocator.prototype.getHref = function() {
    var url = this.getLocation();
    var url_elements = url.split(this.dividingCharacter);
    return url_elements[0];
}
PageLocator.prototype.makeNewLocation = function(new_qs) {
    return this.getHref() + this.dividingCharacter + new_qs;
}

I also have a setContent function that simply takes whatever you pass it and inserts it into the content container div on the page. I'm using the innerHtml property because it's really not the point of this demo. If I was doing it properly I'd probably go for something a little cleverer.

function setContent(new_content) {
    if(!document.getElementById || !document.getElementsByTagName) return;
    var container = document.getElementById("content");
    container.innerHTML = new_content;
}

In the spirit of 'proper' scripting my demo will work with javascript turned off. The links on the page point to content.php, which loads the same content as the AJAX app, but server side.

It's all too easy

So, I'm trying to store the session state in the address bar to allow bookmarking. What can be changed in the URL that won't trigger a page reload? The hash portion. So what I need to do is add my AJAX application's parameters after a #.

There is an additional benefit to this approach: When you click on page anchors, these points are added to the browser's history object so that when you press the back button you're taken back to these points within the same page. That's important because items are being added to the history without leaving the current page.

To make use of this behaviour I wrote written a simple DOM script to change the links on my page into # anchors, with the # portion containing the argument that would have been passed to content.php (the server-side equivalent of this app). This effectively maps the real querystring to a hash pseudo querystring.

Now when the links are clicked, the address bar is changed but the page itself doesn't change. To sync the page content with the URL I've set a Javascript timer to poll the window.location.href property on a regular basis and use any changes to trigger an AJAX content-load action. This effectively de-couples the default browser functionality, so instead of changing the page when a link is pressed, this now happens whenever something in the address bar changes. This means that if we change the URL manually or, importantly, with a bookmark, the page's content is automatically changed to reflect the new url.

To my surprise, it was very simple to get this all set up and working in Firefox. I was even more surprised to see that it worked reasonably well in IE6 as well. Reasonably, but not completely. For some reason, IE wasn't adding my anchors to the history so when I hit back, I was taken off my page and the AJAX app reset its state.

function AjaxUrlFixer() {
    this.fixLinks();
    
    this.locator = new PageLocator("window.location.href", "#");
    this.timer = new Timer(this);
    this.checkWhetherChanged(0);
}
AjaxUrlFixer.prototype.fixLinks = function () {
    var links = document.getElementsByTagName("A");
    for(var i=0; i<links.length; i++) {
        var href = links[i].getAttribute("href");
        var hash = href.substr(href.indexOf("hash=")+5);
        links[i].setAttribute("href","#"+hash);
    }
}
AjaxUrlFixer.prototype.checkWhetherChanged = function(location){
    if(this.locator.getHash() != location) {
        doGetPage(this.locator.getHash());
    }
    this.timer.setTimeout("checkWhetherChanged", 200, this.locator.getHash());
}

Now you're just being difficult

So, IE won't add my modified anchors to the history object. There is a fix that's been in use by Flash developers for a little while that uses frames to trick the browser into thinking that it's loading new pages, and use that to trap and mimic the back button action within the Flash apps. See www.holler.co.uk for an example of this in action.

After reading through the Flash back button fix and remembering Dojo Toolkit's reference to their own back button fix I decided to give iframes a shot. The catch is that the iframe has to be present on the page before the DOM tree is complete so it has to be document.written out inline. It's not the cleanest solution because it means that I need to have a script block in the BODY, which I like to avoid, but as far as I know there's no way around that.

With the iframe in, the mechanism is roughly the same...

Instead of changing the links on the page to # anchors, they are modified to change the src attribute of the iframe, loading a page called mock-page.php with a querystring that contains the same argument as the hash did before. A timer polls the iframe for it's location and if it detects a change then a content load is triggered in the AJAX app, same as before.

This is complicated by the fact that the iframe's src property doesn't change when the back button is pressed. To get around this I've written a little function to sit within mock-page.php and report it's location when asked.

function getLocation() {
    return '<?php print "http://" . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . "?" . $_SERVER['QUERY_STRING'] ?>';
}

When a change in mock-page.php's url is detected an AJAX content load is triggered and the parameters from it's querystring are duplicated into the url, to make sure that bookmarking will still work. It's sounds a bit convoluted but the code is quite straight forward. Except for one thing... IE wouldn't let me access the window.location immediately. I have no idea why... As a simple workaround, I've added a tiny 100ms delay to the firing of the script, which seems to sort it out.

function AjaxIframesFixer(iframeid) {
    this.iframeid = iframeid;
    if (document.getElementById('ajaxnav')) {
        this.fixLinks();
	
        this.locator = new PageLocator("document.frames['"+this.iframeid+"'].getLocation()", "?hash=");
        this.windowlocator = new PageLocator("window.location.href", "#");
        this.timer = new Timer(this);
	
        this.delayInit(); // required or IE doesn't fire
    }
}
AjaxIframesFixer.prototype.fixLinks = function (iframeid) {
    var links = document.getElementsByTagName("A");
    for(var i=0; i<links.length; i++) {
        var href = links[i].getAttribute("href");
        var hash = href.substr(href.indexOf("hash=")+5);
        links[i].setAttribute("href", "Javascript:document.getElementById('"+this.iframeid+"').setAttribute('src', 'mock-page.php?hash="+hash+"');");
    }
}
AjaxIframesFixer.prototype.delayInit = function(){
    this.timer.setTimeout("checkBookmark", 100, "");
}
AjaxIframesFixer.prototype.checkBookmark = function(){
    window.location = this.windowlocator.makeNewLocation(this.locator.getHash());
    this.checkWhetherChanged(0);
}
AjaxIframesFixer.prototype.checkWhetherChanged = function(location){
    if(this.locator.getHash() != location) {
        doGetPage(this.locator.getHash());
        window.location = this.windowlocator.makeNewLocation(this.locator.getHash());
    }
    this.timer.setTimeout("checkWhetherChanged", 200, this.locator.getHash());
}

It's not what you said it's how you said it

I hate branching scripts but I couldn't find a method that would work across all browsers. To make things as easy as possible I've made the fixes objects so they can be plugged or removed as easily as possible.

function FixBackAndBookmarking() {
    if(!document.getElementById || !document.getElementsByTagName) return;
    if(document.iframesfix) {
        fix = new AjaxIframesFixer('ajaxnav');
    } else {
        fix = new AjaxUrlFixer();
    }
}

Moving on

What I've produced here is a simple illustration of a method for storing the browser state in the URL bar, to allow bookmarking, replaced the default browser linking mechanism to use that url-stored state and mimic traditional web behaviour. For a real world application the querystring used is likely to be far more complicated... I've separated out my code into distinct objects to try and make customisation easier.

To see it in action, I have put up a demo page with everything hooked together.

This method could be applied to Flash as well. As far as I know most people are still using the full frames method documented by Robert Penner. Using some Javascript we can do away with that extra complexity to make going back and bookmarking a plug-and-play addition.

Reservations

As I was writing this it occurred to me that what I've been doing here is remarkably like the old Frames hacks from back in the day. Should we be trying so hard to duplicate traditional browser behaviour? If it's important enough to put in the effort to duplicate it then should we really be breaking it in the first place? It's a question that can only be answered on a project-by-project basis but it seems important enough to be worth asking...

Components

index.html
The important part, from the point of view of this article. Contains:
  • Timer object
  • addEvent fix
  • JPSpan AJAX functions
  • PageLocator object
  • AjaxIframesFixer object for IE
  • AjaxUrlFixer object for Firefox and others
test.php
Sets up JPSpan server side.
mock-page.php
Loaded into iframe. Has getLocation function that reports it's current URL.
pageholder.class.php
Simple class used to serve content.
Browser support
Browser Bookmarking Back button
IE6/PC Yes Yes
IE5.5/PC Yes Yes
IE5/PC Yes Yes
IE5/Mac No No
Firefox/PC Yes Yes
Firefox/Mac Yes Yes
Safari1.2/Mac Yes No

If you want to have a play with this yourself, I've zipped up the whole lot ready for download.

Comments

  • Fascinating article, Mike. Here’s a thought: if you allow the href=”#thing” to act as a link to a real anchor on the page (so use return true instead of return false) as well as a trigger for the scripting, then the latest version JAWS has a chance of working with your AJAX too.

    So when you make a change to an area of the page you can link to that area and help guide (some) screen readers (indeed all modern browsers) to the new content.

    by Richard Rutter on June 15 2005, 18:12 #

  • Nice thought Richard.

    by Pascal Opitz on June 15 2005, 18:40 #

  • Ah, hadn’t thought of that! I wonder how hard that would be to add…? We’ve got a thing coming up here soon about AJAX and accessibility so I might give it a go then.

    by Mike Stenhouse on June 15 2005, 18:46 #

  • It isn’t exactly Ajax, but I’ve been considering doing this for S5 as well. You can already use fragment identifiers to go straight to a slide, but the address bar doesn’t change when you move through the slide show. It’s been an occasional request, and if I decide to do it, I’ll definitely look at your code to see how it should be done. Thanks!

    by Eric Meyer on June 15 2005, 19:18 #

  • Crikey, it’s the mighty Mr Meyer! You got me into this stuff in the first place… I think I might owe you a beer.

    I reckon this could have uses outside of AJAX but that’s just what I happened to be talking about when I came up with the idea, and it stuck. I’d love to see people put this concept to use elsewhere! Flash seemed like the obvious one, since it’s asynchronous too, but your S5 app requires the decoupling of the default browser machinery as well, so this might be of some use.

    by Mike Stenhouse on June 15 2005, 19:36 #

  • Mike,

    Thanks, I was in the process of researching all this stuff for my ajax apps. Prototype (Ajax framework built into RoR) needs this. I’ve been working on making prototype fail (ie regular links unless ajax is available) and will think of how to add this as well.

    by Jesse Andrews on June 16 2005, 05:28 #

  • This breaks completely if you turn off Javascript.

    by Jonathan on June 16 2005, 09:40 #

  • Nice stuff. It looks like the code snippets here are wrong though… take the following code:

    for(var i=0; i
    var href = links[i].getAttribute(“href”);

    There’s something missing there ;-)

    Also, I don’t think it’s a good idea to pass on strings of JavaScript code to PageLocator. You’d rather write a little wrapper function to execute that code than starting up the JS parser by using `eval`.

    And Jonathan, I don’t think the app itself will work without JavaScript, so that isn’t much of a problem.

    by Mark Wubben on June 16 2005, 10:29 #

  • Jonathan: Well duh! :rolleyes:

    It’s an interesting technique, although I can’t help thinking there must be an easier way to do it.

    by Matthew Pennell on June 16 2005, 10:46 #

  • Damn, I was just working on a demonstration javascript which does exactly that a few days ago to put on my website, but it isn’t finished yet. So you beat me to it, and that sucks!

    Anyways, a few comments:
    – It would be easier to use window.location.hash instead of window.location.href;
    – Instead of loading actual pages with the iframe, you can use iframe.contentWindow.document.open, write and close to achieve the same effect;
    – And if you use that instead, you can also let it send an event to the script element on load using onload=”” or so you don’t need the 100ms timeout.

    I don’t really have time to look into it further now.

    ~Grauw

    by Laurens Holst on June 16 2005, 10:51 #

  • Jonathan: The AJAX-style functionality breaks but a fallback is provided – the links still go to the content.

    Mark and Laurens: Thanks loads for your input. I’m neither a JS guru nor an AJAX one; I saw a problem and decided to try and solve it. I really appreciate the input of people who REALLY know the components here.

    Laurens: Sorry mate!

    by Mike Stenhouse on June 16 2005, 15:04 #

  • First, for comment 7 (Jonathan) it breaks without js? Wasn’t AJAX all about js?
    (you’re welcome Mike :)

    The technique is cool , i have been looking into it and still might try my own solution for it someday, still digging into js stuff.
    The one thing i didn’t like was the part that the # makes the page jump to the top again (at least at my first view on FF/Linux). Might be something to look into.

    Other than that, great job.

    by mstyle on June 17 2005, 01:38 #

  • I really like the way you have set up this article. How clear can you get writing up where it’s tested and where it works? By the way, I wouldn’t mind (and I’m sure Mike doesn’t either) feedback and improvements from other configurations than the tested ones. Thinking about the Opera version which supports XmlHttpRequest, is that out properly? I’ve lost track with Opera, which is a shame. Or any Linux variations. And please no weird hacked or specialized versions. The ones your mum / your client would use is fine. And a cake. And presents. I also wonder what the limitations on Safari are.
    I think what Jonathan means, is that if you turn JS off, there is no css loaded with the content when you click on the links. But I think that’s due to the demo being a demo, or am I wrong?

    by Matthias on June 17 2005, 01:59 #

  • Well, I can verify it all works perfectly with Firefox in Linux.

    by Sam Kaufman on June 17 2005, 02:38 #

  • Thanks for the killer article man. I will definitely be integrating this into mp3act. I’ve been meaning to do that for awhile now and this is a great solution by the looks of it.

    So thanks again, and nice site! It’s going into the RSS reader pronto.

    by Jon Buda on June 17 2005, 08:15 #

  • Yay! Lovin’ the over use of the eval statement.

    j00 r0x duD3!

    by A visitor on June 17 2005, 14:21 #

  • oooooooooo…

    eval() is evil!

    DON’T USE IT EVER… IT’S A LAME SOLUTION!

    by another visitor on June 17 2005, 14:54 #

  • Cool, well suggest an alternative and I’ll pop it in. Mark Wubben spotted the same thing. As I said earlier, I’m not a JS guru – my interest is user experience, so if you know a better way, I’m all ears.

    by Mike Stenhouse on June 17 2005, 15:09 #

  • @all the eval haters:
    I don’t think eval used on the client side is a big issue, unlike the server side when input variables are evaluated. So I don’t agree with the statement that eval is a lame solution.
    What's the worst thing that could happen? Since anyone can read the source and all the data is already passed to the browser it would be possible to execute anything through javascript:doWhatIWant(with.myObject) anyway ...
    If you want to make the thing “secure” than check the propertyToUse against a list of allowed properties.

    When we move to PHP code that’s a different story. In general you should always make sure that gets fed into a string which will be parsed by eval CANNOT contain anything malicious.

    “Never trust any user input!”

    by Pascal Opitz on June 17 2005, 15:29 #

  • Well…besides precious time and client-side processor-load, ... what IS the worst thing that could happen?

    by Matthias on June 18 2005, 23:34 #

  • Eval isn’t the fastest thing since slicest bread, you might as well want to avoid it if that’s possible. Anyway, Mike, I already suggested a solution :) Here it is again, but with a code example:

    this.locator = new PageLocator(function(){return window.location.href}, ”#”);

    Inside PageLocator you simply execute the passed function, and you get the value.

    by Mark Wubben on June 19 2005, 15:34 #

  • @Mark: good suggestion, and yeah, definetly eval isn’t the FASTEST thing, so yeah, avoid it if possible … But the script doesn’t loop-execute a thousand evals, so I just don’t see the massive impact the one single use of eval would have had … I just don’t see where eval is EVIL …

    by Pascal Opitz on June 20 2005, 02:49 #

  • Eval has some necessary uses, though it’s slow because the parser has to go over each eval line twice: once to evaluate the statement, and once to execute it. Not a huge problem for small scripts, but not the most elegant solution either. I use eval sometimes to escape function literals (var foo = function(){/* bar */};) because they cause syntax errors in some older (now ancient) browsers.

    However, in your case, if you just need to evaluate a string as a variable name, why not use a string index of an associative array?

    if(this.validProperties[propertyToUse]){
    return this.validProperties[propertyToUse]
    }

    by James Craig on June 21 2005, 17:25 #

  • Thanks for the snippet Mark – I really like that idea. I’ve been using the same method in some code I’ve been writing recently but I’d not made the connection back to this example. I would have made the changes the first time you posted but I’ve been incredibly busy…

    Thanks also to James. I’ve always tried to avoid eval but it’s been so long that I’d actually forgotten why. The reason I used it here is because I couldn’t think of a way to make the wrapper flexible enough to monitor a variety of properties. I have a tendency to get hung up on code flexibility… There’s an interesting article on exactly that by Marcus Baker in PHP Architect from back in February.

    by Mike Stenhouse on June 21 2005, 18:38 #

  • Good article Mike.

    If only Opera could fix their document.location bugs already.

    And yes, eval is evil. Any time you see it you should ask yourself why? The only valid reason to use it is to allow a user to enter code that you want to evaluate. In your case it seems what you want to pass as an argument is the location object (or as previously suggested a function returning the object in question).

    by Erik Arvidsson on June 21 2005, 23:08 #

  • This is a very classy solution, I like it.

    I wonder if someone could make a GMaps implementation where each drag results in a ‘reload’ to the same page, but with javascript that scoops up the request, contained entirely in the #clause, and re-find the location… ???

    by Mike Purvis on June 23 2005, 06:40 #

  • Excelent Solution!!!

    by Ignacio on June 23 2005, 17:53 #

  • Mike: That’s exactly what I’d like to see! The reason I did this in the first place was because Google Maps wasn’t easily bookmarkable… Maybe a Greasemonkey script?

    by Mike Stenhouse on June 23 2005, 20:41 #

  • This demo (the download able) doesn’t work for me. The test.php is missing. Maybe I understood something wrong?

    by Vert on June 28 2005, 09:26 #

  • Sorry about that Vert – don’t know when or why I removed test.php from the zip. It’s back in now so give it another go…

    by Mike Stenhouse on June 29 2005, 02:22 #

  • I must be the only mac user to post. The script doesn’t seem to work on either Safari or IE5 for me.
    In safari, the address bar does what it should do, and I can use the back button the way you intended it to be used. However, the content on the page does not revert to previous states when the back button is pressed.
    On IE, the script works, but clicking on the side nav seems to dissacociate the page from it’s stylesheet,

    by Sholom on June 30 2005, 09:16 #

  • The script doesn’t work in iE/Mac – I’ve stopped developing for it as of this year. It’s usage for the sites I work on has dropped below the 1% threshold. When you click on the links you are taken to the static version of the content, generated server side, which I’ve not styled.

    This did work on Safari 1.2 but I’ve not tried it on the newest release. Well, I say work… See the browser support table at the end of the article. Bookmarking worked but the back button remained broken. Maybe 1.3 has changed something…

    by Mike Stenhouse on June 30 2005, 16:00 #

  • I say eval() is a necessary evil since Javascript has almost no support for reflection. (No way to dynamically call functions, dynamically assign variables, etc.)

    by hao2lian on July 2 2005, 04:23 #

  • Good work Mike, this has been an issue of mine for the MP3 site – i’ll be in touch. Glad to also hear your thoughts on IE Mac – i’ve been trying to (unsucessfully) steer clients in that direction. Easy…

    by Mr Spooner on July 8 2005, 16:39 #

  • I wrote my script mostly from scratch, but I got the concept from here, I think it’s a little simpler than yours, and may have a few better methods. chudosok.info 2.1
    Any comments or suggestions?

    by Nobody on July 9 2005, 06:55 #

  • Hi. This leads me to wonder if the same solution can be used for solving similar problems for frames which more or less achieve the same thing if you are just using ajax for bringing in a navigation bar that doesn’t keep refreshing.

    by jw on July 16 2005, 03:51 #

  • In comment 7, Jonathan made an important point, this completely breaks if JS is turned off, the point is important because it does not have to break!

    in the links pointing to content.php you can add a parameter to the url hash=n&nojs=true, in php if you see this parameter you also include the header and footer of the page, this way people who have js disabled will be loading content.php the old way AND getting the same page (with the black strips on top and bottom) , for people with JS enabled the href is rewritten using javascript (removing the nojs=true and using the # hack) and they will use ajax just like they are doing now!

    I hope this was clear enough!

    by Pat on July 21 2005, 09:11 #

  • I think I see what you mean Pat. As it is, if you turn off your JS, go to the demo and click the links they will still bring back your content, via a separate page that uses the same content-generating class server side but without any styling. I didn’t hook that page up to my CSS because my interest lay with the front end and the usability problem presented… It’s a separate page so there’s no need to pass in extra querystring params. To be honest, the server side bit isn’t very elegant but my main focus was the front end.

    There are enough great suggestions here to warrant an upgrade to the article but I just haven’t had time to do it, although Nobody (35) may have beaten me to it anyway!

    by Mike Stenhouse on July 22 2005, 02:10 #

  • If someone could tell me where I can find the Ajax part please do, but this has nothing todo with Ajax. Do not want to be offence, but this solution is a definite no go when utilizing xmlHttpRequest. Good for making ignorant people all excited but also only usefull if you can really set a history object with an iframe and totally not useless in a so called Ajax application.

    There is no fix, and it is technically impossible within current browsers. The use of Ajax on the frontend is the only thing that is wrong. People use it in the wrong way.

    Use it because you need it, not because it is cool.

    by M. Schopman on July 22 2005, 21:56 #

  • Mr. Schopman, I think you didn’t propperly take time understand the scope of this article. Mike is not coding an AJAX application here, but pointing out a possible solution for a common problem occurring with AJAX applications.
    This has nothing to do with beeing cool but is an approach for all those who do use AJAX but find that the back button isn’t just a useless thing in the top corner of the browser.

    Whether people that are actually building AJAX applications and maybe incorporating this solution into their AJAX application are ignorant this might be your point of view, but we don’t necessarily agree with you at that point.

    However, the only useful points you really made are

    a) You should use things because they make sense
    b) You might not be able to use an iframe in your application, therefore the solution mike suggested could not be for you

    To all other posters in here, please watch your tone, I don’t think it’s necessary to be as impolite as Mr Schopman. We are no big fans of editing user comments, but we are no big fans of a rude tone either

    by Pascal Opitz on July 23 2005, 16:08 #

  • Yep, as Pascal said, this article is less about AJAX itself and more about usability. If anyone needs a reality check on AJAX and its related technologies I highly recommend Marcus Baker’s article Listen kids, AJAX is not cool.

    by Mike Stenhouse on July 25 2005, 17:00 #

  • When I go to the demo page, http://www.contentwithstyle.co.uk/resources/ajax-nav/#1, I get errors in a dialog saying only Only variables should be assigned by reference etc. in both IE and FireFox. Any ideas?

    by tester on July 27 2005, 18:38 #

  • Hi tester.

    This is a problem with JPSpan and PHP 4.4. My hosting provider just upgraded from 4.x to 4.4, therefore the unexpected error.

    We’ll fix that ASAP.

    by Pascal Opitz on July 28 2005, 18:26 #

  • Hi,

    Thanks for writing fix for back button problem with AJAX.

    Well, i am trying to fix this problem by utilising your code but I am not using JPSPAN function for AJAX. I am simply using XMLHTTP object and its function to make request to my server side php and getting html data ( i don’t need xml data).

    I am not sure whether your fix will work for all the AJAX code. But i will appreciate if you change it to be compatible and generic for all AJAX. As of now, with your scripts I am totally confused to use what and where? Can you please write a small paragraph on how to plug this into any AJAX code. I am not JS expert nor I am AJAX expert.

    And I do have real time monitoring system which uses ajax and in my page there are four frames which are requesting to server and loading data in real time.

    Any help in this will be greatly appreciated.

    Cheers!

    by Dipesh on July 29 2005, 00:59 #

  • The bits of the code that call the back-end are ‘function doGetPage(i)’ and ‘var PageHolderHandler’. I’ve not looked into exactly how they’re hooked up to my PHP – I’ve let the magic of JPSpan take care of that for me. If your code is OO then I highly recommend JPSpan – it exposes your server-side methods to your client side code in a matter of minutes…

    You say you have 4 frames… Do you mean frames, as in the HTML element? That might break my fix since I use an iFrame exploit to get the whole thing working in IE.

    by Mike Stenhouse on July 29 2005, 01:58 #

  • Hi Mike,
    Thanks for responding and informing me about the firing methods.

    Well, I will try to use that into my code and see whether I can use it or not.

    Yes, i do have 4 frames (html) element in my page and each of them is auto refreshing. Out of these one is hidden (triggering frame) and other three are data pulling frames from the server.

    Well I am trying to support my application on FF first and if that works then 90% work done.

    Thanks once again.

    Cheers!
    Dipesh

    by Dipesh on July 29 2005, 19:17 #

  • If you want, Dipesh, you can look at the source code on chudosok It uses pure XMLHTTPRequest for getting back html.

    by Nobody on July 31 2005, 04:07 #

  • The problem with http://www.donotremove.co.uk/extra/ajax-nav/ is that you can’t open in new window in Internet Explorer and it won’t work at all in Opera. chudosok works in all except if you browse too fast in Opera. It’d be nice if you tried some of my code.

    by Nobody on July 31 2005, 04:23 #

  • One more thing, I don’t want to say I’m better, after all I did get the idea from you. Thanks.

    by Nobody on July 31 2005, 04:25 #

  • No offense taken! I look forward to examining your method when I’ve got a little more time…

    by Mike Stenhouse on August 1 2005, 15:28 #

  • Hi Nobody,

    Well i tried to go to your site to see the generic solution. Is your site in some different language, i can’t understand those language ? I went to the following site:

    http://www.chudosok.info/21/

    My problem is with the back button, i have already created real time monitoring system using AJAX.

    Looking forward to hear from you.

    Cheers!

    by Dipesh on August 1 2005, 22:51 #

  • The site is in Russian, but the javascript isn’t, lol. The information on the website has nothing to do with ajax, but it loads the data via ajax and also supports the back button. I’m saying you can take the source if you like, sorry that’s it’s not commented though.

    God Bless.

    by Nobody on August 2 2005, 02:25 #

  • Ok, I made a cut-down, half-English, commented version to make it easier on you.
    http://www.chudosok.info/commented/

    by Nobody on August 2 2005, 03:43 #

  • Hi Mike

    The code fix is good and would do the job for simple pages. The way I have written my ajax application is that it has a mvc pattern on the browser tier and performs view rendering on the client.
    this should have been ” backable ” using ur style but the issue we face is with handling multiple views..
    a user interaction triggers view updates in multiple views on the page. so a back button click or a bookmark operation would involve remembering the state of all the views.
    any ideas on such a scenario..

    by shashank on August 2 2005, 10:58 #

  • hi,

    Thanks for replying and putting commented files on your site. Greatly appreciated.

    It is still customized to your page, (it has all the elements which you have created in your html) and it is tailored like that. I am not JS expert, but as far as I could make out, following javascript functions must be related to page history :

    1) chkURL() > This function you must be calling on load. Am i right ? This is so that it can check the URL and store it.

    2) getPageN() > Get this page number from the history?

    3) getPageName() > This is for your contact page thing?

    4) getPage() > requesting this page and displaying it in the browser?

    Well, the major problem which i see in using this is I am using 4 frames. There is no event based display of the pages, I am refreshing screen if something is happening at the back end and displaying it to the client. For this I have 4 frames in the page, which has different data and I am updating this data.

    Does your solution still fits into my architecture?

    Thanks once again for writing commented version of your code.

    Regards,
    Dipesh

    by Dipesh on August 2 2005, 19:58 #

  • chkURL() is a like a timer loop that checks whether or not the url has changed, historywise or not. It is started up 100 milliseconds after load. The extra ie.htm and ie.js help Internet Explorer change the url when the back or forward button is clicked.

    getPageN() is fired on url change, which checks if the url is valid, then tells getPage() which page to load and display.

    getPageName() disects the url to get the name of the page. This is used for loading the needed page, for checking whether the url has changed, etc.

    True,
    function getPageName() is invloved in the history.
    function chkURL() is one of the main functions involved in history, but also in navigating by links.

    var lastURL stores the last url to check against when navigating.

    ie.js and ie.htm which are included in this manner:

    and fired at startup like:

    by Nobody on August 3 2005, 00:16 #

  • oops, part of my post got cut off because of the tag stuff

    chkURL() is a like a timer loop that checks whether or not the url has changed, historywise or not. It is started up 100 milliseconds after load. The extra ie.htm and ie.js help Internet Explorer change the url when the back or forward button is clicked.

    getPageN() is fired on url change, which checks if the url is valid, then tells getPage() which page to load and display.

    getPageName() disects the url to get the name of the page. This is used for loading the needed page, for checking whether the url has changed, etc.

    True,
    function getPageName() is invloved in the history.
    function chkURL() is one of the main functions involved in history, but also in navigating by links.

    var lastURL stores the last url to check against when navigating.

    ie.js and ie.htm which are included in this manner:
    {!—[if IE]}
    {script type=”text/javascript” src=”ie.js”}{/script}
    {![endif]—}
    and fired at startup like:
    {body onload=”try{if(ieLoader){ieLoader();}}catch(e){}...

    just replace {} with the the tag thingies

    are required for it to function in Internet Explorer and are only for history, so all the functions there are for history.

    I think what you’re doing is similar to how gmail auto-updates the user’s inbox. If you need people to navigate back in your app, this wil work. All you need is to have people navigate by page.htm#wheretogo and have the chkURL() fire a function to deal with the wheretogo when it changes.

    And you’re welcome.

    by Nobody on August 3 2005, 00:21 #

  • doesnt’ works for opera 8 browser :/

    by Xavier on August 29 2005, 09:06 #

  • >doesnt’ works for opera 8 browser :/

    Mine or Mike’s?

    by Nobody on August 30 2005, 18:29 #

  • —> doesn’t work if you turn Javascript off

    so when are people going to stop saying this about AJAX stuff? You know Async JAVASCRIPT and XML?

    We know that AJAX stuff doesn’t work if Javascript is turned off. WE KNOW.

    If Javascript isn’t turned off then we don’t need to worry about bookmarking where we are in an AJAX app do we?

    Sorry…just tired of seeing that comment EVERYWHERE.

    by Steve on September 28 2005, 20:22 #

  • “What can be changed in the URL that won’t trigger a page reload? The hash portion.”

    Brilliantly simple. Genius, even. Thanks for the great article!

    by James on October 20 2005, 05:20 #

  • @Nobody – Could you please show the code for the /commented/data.php file.

    by Gavin on October 23 2005, 20:25 #

  • Mike:

    The error:

    [Server+Error][2000] Only variables should be assigned by reference
    Method called: pageholder.as html()

    still exists, even though it was committed to being fixed back in July (ref. comments 42 and 43).

    I would really be interested in looking at this as I have an urgent need for some reasonably way of handling the back button issue, even if it’s not perfect.

    Any chance this can be addressed in the very near future?
    – Thanks!

    Jim

    by Jim on October 27 2005, 04:50 #

  • It actually was fixed! That error is due to some changes in PHP4.40 that have affected the version of JPSPAN that I’m using. I moved the demo to my webhost that until this week was running on PHP4.3 but it looks like they’ve now upgraded. I’ll fix it AGAIN. Sorry for any delay Jim.

    by Mike Stenhouse on October 27 2005, 06:41 #

  • Right, I’ve moved the demo to my dev server so it’s up and running again, this time under PHP5. There is a bug report on the JPSPAN sourceforge page about the PHP4.4.0 problem but nothing’s been done about it yet. I guess Harry’s busy…

    by Mike Stenhouse on October 27 2005, 08:42 #

  • Mike:

    Thank you!

    I have scoured the web and yours seems the most viable solution.

    Those of us serious about using modern technologies in our dynamically generated web apps must have a reliable way of dealing with the legacy issues that will always be with us – those left over from the days when all web pages were static.

    I am most anxious to view/study your solution.

    Thanks, again.

    Jim

    by Jim on October 27 2005, 13:32 #

  • Mike:

    Can you share what the PHP 4.4.0 fix is?

    Thank you.

    (You’re right about Harry. Haven’t seen him any of the usual places for a very long time.)

    Jim

    by Jim on October 27 2005, 22:07 #

  • Jim, if you look up the comments a bit you’ll see a link from a chap calling himself ‘Nobody’ to an implementation he’s come up with. He says it’s simpler than mine but I just haven’t had the time to dissect it myself. Might be worth a look if you’re starting from scratch though…

    From what I can gather the good folk behind PHP have decided to tighten up their pass-by-reference handling and creating new objects. I don’t know the ins and outs of it to be honest. I had several days of listening to Marcus complain about the changes he was having to make to SimpleTest but that’s about my lot! There’s a decent explanation on SitePoint though: PHP 4.4 Minor Gotcha. Hope that helps…

    by Mike Stenhouse on October 28 2005, 08:41 #

  • Mike:

    Interesting reading. Thanks for the link.
    – Thanks!

    by Jim on October 28 2005, 17:36 #

  • > Could you please show the code for the /commented/data.php file.

    Sorry, I’ve been gone for ages, here’s the content in data.php: http://www.chudosok.info/commented/data_php.txt

    by Nobody on October 29 2005, 17:52 #

  • This method works okey for me, but when i try to include something more than a plain text in ‘pageholder.class.php’ gives me problems.

    I can’t include into this class complete pages becoming of every directory. Do everybody know how to fix this problem?

    by peter on November 21 2005, 11:54 #

  • Thanks for this. I will have to adapt this to my AJAX app . At this time I only have one link that allows outside linking. I don’t know if I will implement the support for non javascript seeing as without it my app is no good anyway.

    by Coder2000 on November 30 2005, 16:43 #

  • Hi, nice article. I just want you to now there is this really_simple_history_framework that I use and seems to be working pretty cool. http://codinginparadise.org/projects/dhtml_history/README.html
    I think the approach is the same as what you do, but you mught find it interesting to check out.

    kind regards.

    by Rik on December 2 2005, 06:52 #

  • The bookmarking system described here works only for javascript-enabled browser. If you follow the #-kind-of-link in the browser without javascript, the result will be weird.

    Is there a way to get access to the hash portion of the url from server? If it’s possible we could fix the problem…

    by colobot on December 5 2005, 09:38 #

  • Is this method also applicable for DOM scripts eg a toggle which relies on #hash links that show/hide a div container.The hashlinks which toggle the on/off state are not visible in the actual addressbar either.

    by repeater on December 5 2005, 19:19 #

  • I’m using js link a bit differently

    [a href=”some.link” onclick=”goUrl(‘some.link’);return false;”]Some Link[/a]

    In this way there is a fallback/failover suport if js is disabled.

    My problems are
    1. back button
    2. bookmarking current page

    How do you suggest to address this case ? Do you thing I can adjust your solution to my problem ?

    Thanks,
    MC
    www.goodstockimages.com

    Ps.
    BTW I’m using AjaxAnywhere http://ajaxanywhere.sourceforge.net/

    by MC on December 7 2005, 13:21 #

  • wow… amazing… I’ve looking for this and now… I understand it… thanks.

    just a brazilian newbie.

    by Juan Hafliger on December 11 2005, 06:33 #

  • FYI – There has been a really nice implementation of this approach by a big company. Yahoo! has done this well with their new maps beta. The maps (using flash) are very dynamic but the url always remains accurate and “cut and pasteable” via use of the hash.

    by Udi Falkson on December 22 2005, 17:26 #

  • @MC

    Just make you js link do “window.location.href = ’#lalala’;”

    So, it will still do the navigating thing.

    by Nobody on December 14 2005, 23:12 #

  • I want to develop a search results page, in which i want to display the results depends on price. I want to use one slider which contains the price range. when the user drag this slider, i have to display the results in this price range only.

    by venkat on December 21 2005, 08:21 #

  • A bit off topic, but I’m developing a file manager which has file searches which get posted from a form. I then use window.location.href or window.location.hash to write the search string to the URL. This works fine, except in Safari, where the page goes into a permanent ‘Loading’ state. Any ideas?

    by Jeff P on December 29 2005, 03:49 #

  • Man I LOVE YOU !!! Thanks for the this great article !

    by danux on December 29 2005, 08:39 #

  • This solution is right but still not 100% because in IE it doesnt let you change the hash programmaticly .

    check erik.eae.net/playground/hashlistener.zip for the solution

    by Tastaturbeschmutzer on January 9 2006, 17:06 #

  • Hi,
    I’m the admin of the opensource project “thephpbible.sourceforge.net”
    thephpbible allows webdesigners to quickly and easily integrate a simple Bible into their church or organizations web site.
    We need someone to add this functionality to our project.
    If anyone is interested contact me (bywave AT gmail.com)

    Thanks

    by thephpbible on January 16 2006, 02:57 #

  • hey there tastatureschmutzer

    excellent work. i got yours working on firefox on mac with ease. how do you get it working on a pc running internet explorer.

    great stuff.

    by autonomous019 on January 27 2006, 12:08 #

  • Has anyone got this working with flash yet? we flash guys really need these basic browser functions

    by Gareth on February 6 2006, 17:25 #

  • This has been done with Flash before, but this article is specifically about doing it with the XMLHttpRequest object.

    by Nobody on February 6 2006, 21:24 #

  • It already is possible in Flash – that’s where I got the basic technique from! For an example of it in action see the Holler site…

    by Mike Stenhouse on February 7 2006, 06:47 #

  • The Holler site only seems to address the back button issue in Flash, though, not bookmarking. Robert Penner’s solution also doesn’t support bookmarking (unless I’m missing something). Perhaps it would be worthwhile to change the “The original bookmark/back button fix, as used by Flash developers for a little while now” phrase in the article to only mention the back button? Sorry if I’m wrong about any of this.

    Thanks.

    by b on February 7 2006, 18:55 #

  • You might want to have a look at the source of the track attack website, it’s a flash site with working back button and the possibility to bookmark a page, and it’s developed in a similar way to Mike’s approach.

    by Matthias on February 8 2006, 05:16 #

  • Thanks very much, Matthias, I really appreciate it.

    by b on February 8 2006, 12:02 #

  • So how do you get this to work without JSpan and PHP, like if you for example use ASP.Net like me??

    What does doGetPage do??

    Why don’t you update the script with the suggestions, suggested amongst other by Mark?

    by Henrik on February 15 2006, 10:44 #

  • Henrik:

    How you hook up your Ajax to your back-end technology isn’t the point of this article – that’s why I used an out-of-the-box solution like JPSpan to deal with that aspect for me. Here I am only concerned with how you trick the browsers into adding to their history and proposing a way to get bookmarking working. This method actually isn’t limited to Ajax either – Matthias has got it working for Flash and it would work fine with Eric’s S5 too.

    doGetPage is the function that gets content from the server side. If you were doing this for yourself that is the function that you would have to change. pageholder is a PHP class on the sever side whose method ashtml returns a string of HTML. It’s not documented in the article because, as I said before, the transfer method is irrelevant. It’s all in the zip file though if you want to take a proper look for yourself…

    I’ve not updated the script for the same reason I’ve not written any new articles recently: time! I’ve just got too much work on to spend much more time on this, which is done completely for free. When I get time I’ll do some kind of redux but until then I would highly recommend checking out Nobody’s comments above – he’s come up with another method for doing the same thing, based on this one, and Robert Nyman’s AJAX Source Kit, which looks really good.

    by Mike Stenhouse on February 15 2006, 12:22 #

  • With a address like http://foo.bar/#foo/bar/ , readability is improved a lot. Compare this with a google map address :-)

    by Apoorv S on February 11 2006, 05:27 #

  • i am looking for smaller companies in united kingdom and USA, that are using this web application called AJAX, were do i find these companies

    by Precious Edokpolo on February 17 2006, 00:19 #

  • Hey guys!

    First of all, thanks a lot for a really useful article, Mike. It gave me exactly the help I was looking for. I’ve been implementing your methods in a new website I’m working on for Persnickety Tim’s Coffee, but I’ve got this freakish behaviour going on in IE version 6 on the PC, and I can’t for the life of me figure it out.

    You can see what I’m talking about at http://test.ptims.com (just open it with IE). The only AJAX-enabled buttons right now are the Buy Coffee links and the Gift Baskets link. Click around between those pages, maybe use the back button once and then click the buttons a couple more times—first of all, you’ll see that the window title changes, while the only thing Javascript is changing is the window.location property (I’ve also tried window.location.href and window.location.hash). Second and more importantly, IE goes to a blank page and the top left icon (above the File menu) changes as well.

    I’ve been scratching my head for a week on this one. If I comment out the line that changes window.location (or .hash or .href), things work great—the back button included. But once I try to change window.location, it freaks out. I’ve tried changing window.location after the iframe src change, before it, I’ve even had window.location change a couple seconds before changing the iframe src—the error keeps coming up.

    I’m on the verge of foregoing the bookmark functionality, but I thought I’d see if any of you great minds are up for a challenge. I’ll be grateful for any help. Thanks!

    by Gus Welter on March 11 2006, 00:27 #

  • Hello Mike and thank you for your answer. I definately know what it’s like not to have time, and I guess in the end it comes down to my own ability to do tricks with javascript, whether I get it to work as I intend to or not.

    I think my basic problem was that I didn’t really see what type of data that function returned, and it didn’t say it was a transfer function. Thanks for the explaination.

    I’ll have a look at “Nobody”’s comments above, and I have already had a look at Robert’s Ajax implementation – it look really great, and I’m sure I’ll have a look at it when I get through the exams :p.

    by Henrik on February 22 2006, 03:17 #

  • Hey mate,
    this works great!
    I modified it a bit in order to make normal anchors work as well and it worked out perfect.

    I tried it in netscape 7.1 as well just for checking compatibility and unfourtunatley it does NOT work there…though netscape is mozilla based. Very strange.

    Maybe u can work this out.

    Cheers, Florian

    by Florian on January 29 2006, 06:25 #

  • Hi can some1 please help me, I tried to log into an account of mine but this keeps coming up: “you have been log out of your account as the use of the browser back button is not supported”. This just suddenly happened and I’ve no idea how to fix it. Please help!!!!!!!!!!!!

    by Phoenix on January 29 2006, 07:28 #

  • I am using Brad Neubergs solution for retaining the back-button functionality, and it works very well.

    There is, however, one thing I do not like (but seems to be IE’s standard behaviour): the selection box on the back- and forward buttons (for freely selecting any visited page instaed of going just back and forth) always show the full page URI (which is usally so long, it is meaningless, as you only see the server address), where I would only like to see the visited pages title.

    And to my great delight, your demo app does just that: it displays the entry “Dummy Page” there!

    How did you achieve that????

    by Winfried Kaiser on March 2 2006, 06:49 #

  • Great article!

    As far as I understand this, together with at lot of good comments from others, such as “Nobody” , may solve my problem. It’s great that you people are willing to share good ideas with others!

    I may not use your script, but the idea itself was what I needed.

    My users, wich are puples and theachers in Norwegian shools, love the AJAX-solution but miss bookmarking and the buttons. Dealing with that kind of users we really need to take care of this issue. Your solution just gave me hope :)

    My only concern is that this idea triggered me and probably will keep me awake the rest of the night ;)

    Hopefully the browser vendors will give us some solutions, but that may take some time.

    Thank you for taking the time to write the script and the article!

    Winifred:

    I guess the reason why you see long URL’s in IE’s selection box is that you have ben browsing pages whitout a proper title. In my selction boxes I see readable titles when I have been visiting pages with titles…

    by Frode on March 2 2006, 20:08 #

  • Hey folks ..

    i’ve a problem .. stuck in the deep end with php and can’t find a life-jacket ;P

    i’m trying to serve a php page with ‘pageholder.class.php’ .. but can seem to locate a suitable cmd to insert the page into the div ..

    i’ve tryed a ‘echo “document.mosteverything(myphp.php)” ’

    but no avail ..

    also thought a ‘document.write’ with a ‘include_once’ served in php might do .. but alas no ..

    fast trying to learn js an php so any helps would be most appriciated :)

    ta

    bren

    p.s : i’m sure this is going to be a duh wheres my brain gone but deadlines do strange things to a mans minds ..

    by Brendan on April 6 2006, 21:49 #

  • Great work, great article, thanks to share all this code!
    i’ll try to learn from it, my website is in need of a liftup :)

    by Ben borges on April 9 2006, 12:48 #

  • Dont make it with JPSpan. I am newbie and I donnot understand!

    Please!!!

    by david nguyen on April 18 2006, 00:00 #

  • David: Sorry if you don’t understand but this article isn’t really about Ajax, which is why I used JPSpan – to take that aspect out of the equation. The point here is the javascript to fix the back button and enable bookmarking.

    There are far better people than me out there writing about implementing your own Ajax apps…

    by Mike Stenhouse on April 18 2006, 12:39 #

  • I know I am a little late to this discussion. I have been following it but never commented on it.

    My approach so far has been to use a bootstrap page of some sort (pick your flavor of server side language)

    ALL requests are routed through the bootstrap page using your favorite rewrite engine. (mod rewrite in my case) The page then parses out the path in the address bar and stores the values.

    Each controller knows that if is invoked with params that are passed AFTER a # that the request is “ajaxy” and behaves as appropriate. If the url doesn’t have a hash the backend composes a page and returns it.

    The js on the front simply doesn’t fire if it isn’t fully supported. Once it fires it finds all link elements that allow ajax style updates of content (via class, id, rel, rev, hash tables of ajax listeners .. doesn’t matter ) and replaces their real url with the same exact string preceded by a #.

    Without ajax support the links are real url://domain.com/foo/bar style links. With ajax support they are url://domain/#foo/bar style links.

    It is all proof of concept code so far. But if I get it working more nicely I will of course share.

    Sorry if this is off topic. I started to ramble.

    by Abba Bryant on April 18 2006, 16:37 #

  • Your demo doesn’t work in Opera at all (links are unclickable), but it looks nice in Firefox.

    by Shadowhand on May 2 2006, 13:10 #

  • I actually read the article and didn’t find it very helpful, so I wrote my own back button AJAX fix with PHP.

    It is available here

    It is simpler to use. Assuming you have PHP setup, you can just pop it onto your webserver and see it work.

    It has a noframes fix, so that if someone chooses not to use frames, the site will still work, even if the frames don’t fix the back button.

    The example I wrote has code highlighting so it is easier to read as well. The main advantage my example is you can just read the code and see how it all works.

    by White Marker on May 11 2006, 16:34 #

  • Your demo worked fine in Opera and IE7. Great work, and excellent article. In my opinion the most important browsers :)

    by bubblez on May 30 2006, 02:11 #

  • I’ve been struggling with the back button issue for over a week now and I’m not any closer to a solution. The site I am building is still at it’s early stages and I haven’t yet added even support for non-IE browsers. I do not know anything about PHP and I can’t tell if I can even implement the solutions discussed here since I already came up with my own bookamrking scheme and I’m using frames besides. If anyone has any suggestions, please let me know. I can post any code that people would want to see or even allow anonymous read access to the folder that contains all of my current source.

    by Conn Buckley on June 14 2006, 17:45 #

  • Great article! Helped me out tremendously. I used it to create a much simplified version for Flash.

    StoneArch – Page 1

    var currentUrl = “”;
    var intervalId = 0;

    // Start the timer and read the initial Url
    function init() {
    currentUrl = window.location.href;
    intervalId = window.setInterval(checkForUrlChanges, 10);
    }

    // Add to the history
    function addBookmark(page, title) {
    document.title = title;
    currentUrl = “mockpage.html?id=” + page;
    window.frames[“mockPage”].location.href = currentUrl;
    }

    // Check for changes in the Url, if they occur update the Flash movie
    function checkForUrlChanges() {
    if (currentUrl != window.frames[“mockPage”].location.href) {
    currentUrl = window.frames[“mockPage”].location.href;
    var items = window.frames[“mockPage”].location.search.split(”=”);
    thisMovie(“AnchorTest”).gotoBookmark(items1);
    }
    }

    // Find the Flash movie
    function thisMovie(movieName) {
    if (navigator.appName.indexOf(“Microsoft”) != -1) {
    return window[movieName]
    }
    else {
    return document[movieName]
    }
    }

    by David LaTour on June 14 2006, 15:58 #

  • hi all
    a very good article but can anyone suggest some basic article if we not use php but wish such back and bookmarking facility in iframe
    Thanks & Regards
    saurabh

    by saurabh gandhi on June 9 2006, 02:59 #

  • So many comments! I’m very pleased to see that you tend to this article and respond to the comments Mike. Kudos! Between your solution, and the information you’ve provided about it, the comments, the answers, and the alternate solutions proposed, including Nobody’s, I’m sure I’ll be able to put together a portable collection with this functionality to fix the apps that I’ve written. I’ve been meaning to consider back buttons and bookmarking, but it wasn’t until today when my girlfriend pointed out those exact issues with my new photo search that I realized it’s time to take them seriously!

    I know this isn’t the place for advertising AJAX apps, but I’ve gotten into writing them recently, and I’m pretty excited about my photo search app: www.jcbeck.com/photos/javascript.asp
    This is the “beta” url, which shall remain here, but I may put this “live” at some point either at this url or another. Thanks for those who stop by!

    by Jason Beck on June 28 2006, 17:04 #

  • Peter: This approach is unobtrusive so your links should all be in the standard server-side form with normal querystrings – something like /yourpage.php?id=4. At runtime the DOM script then runs through and hijacks them, turning them into /yourpage.php#id=4. Google will see the ? ones while the user interacts with the # versions. Does that make sense?

    by Mike Stenhouse on September 13 2006, 11:34 #

  • Hi,

    Has anyone noticed that using the rshf in IE you see a loading bar in the status bar when navigating the o’reilly mail example (
    http://shrinkster.com/i51 ). This seems to go against the ajax concept of not seeing any page refresh/loading?

    Any thoughts appreciated.

    Thanks
    Kieran

    by Kieran on September 10 2006, 14:33 #

  • I’ve been struggling with the back button issue for over a week now and I’m not any closer to a solution. The site I am building is still at it’s early stages and I haven’t yet added even support for non-IE browsers. I do not know anything about PHP and I can’t tell if I can even implement the solutions discussed here since I already came up with my own bookamrking scheme and I’m using frames besides. If anyone has any suggestions, please let me know. I can post any code that people would want to see or even allow anonymous read access to the folder that contains all of my current source.

    by Amit Aher on July 27 2006, 09:59 #

  • Hi Nobody,

    i think i just figured it out.

    It’s in ie.js, the function chkHrf()

    i changed window.location.href = ”/21/#” + nhrf; to
    window.location.href = ”./test.htm/#” + nhrf;

    Let me know if i am on the right path, coz that works just fine. Thought i should put it in here so that others can use it as well…

    Please let me know if i have to make any changes anywhere else. For a working demo, have a look at
    http://www.webhostingfx.com/gtek/newajaxnav/test.htm

    Cheers,

    by desperado on August 13 2006, 22:41 #

  • Hi, I have been following this article and responses for a few months. Although I feel that the solution you present is nothing short of brilliant, I have some reservations regarding Google and SEO. Will Google uniquely crawl and add to its banks urls of the type generated here? i.e. does Google see url#bob and url#mary as two unique links with unique content? Any comments would be highly appreciated.

    by Peter on September 13 2006, 11:23 #

  • Sweet! I was just looking for something like this—came here via google. That’s pretty sweet, man. I can’t wait to adapt it for my stupid project…

    by Jon Roig on August 1 2006, 21:32 #

  • Hello,

    FYI:

    The demo provided does not work on IE6 WinXP SP2 (IE 6.0.2900.2180.xpsp_sp2_gdr.050301-1519)

    but this one provided by nobody does.

    by dnk.nitro on August 29 2006, 10:06 #

  • It works fine for me in IE6/XPsp2…

    by Mike Stenhouse on August 29 2006, 10:23 #

  • >I say eval() is a necessary evil since Javascript >has almost no support for reflection. (No way to >dynamically call functions, dynamically assign >variables, etc.)

    Wow…

    x? y() : z();
    x = y || z;
    x();

    x = a? y : z;
    x = x || y;
    if(x) var y = z; else var y = b;

    I thought that was all quite easy.

    by Refinancing Guy on August 4 2006, 16:47 #

  • Hi Nobody,

    First and foremost, thank you for the script, people like you are life savers for people like me. But i need your help.

    I have setup your pages on my server, but it seems the back button doesn’t work proplery. Can you please guide me.
    [link] http://www.webhostingfx.com/gtek/newajaxnav/test.htm

    This is what happens:
    When i click the back button it goes to
    http://www.webhostingfx.com/21/#home

    How can i change this?

    Thanks a lot
    desperado

    by desperado on August 13 2006, 22:04 #

  • Hi Mike – don’t post this – just responding to your response re SEO and Google: I feel like a righteous ass – my brain bypassed that section of the article completely. How weird… I’m a bit freaked out ;-) Anyway, you make total sense. Thanks again for a truly brilliant solution. I’ll send you a link to my site once it’s done so you can see another implementation in action. Best wishes. Peter.

    by Peter on September 14 2006, 09:19 #

  • The demo works fine in Firefox 1.0, but try this in Firefox 1.5 (PC or Mac):

    1) Open the demo
    2) Click on page 2
    3) Open any page outside of the demo (e.g. Google)
    4) Click the back button

    The hash should say #2 and page 2, but instead it’s page 1 and no hash. It actually goes back to the hash value (or lack thereof) when the page was first opened. If you try this test by going to page 3 directly first (with #3) in step 1, at step 4 it’ll be page 3 again.

    It appears to only be a “cosmetic” bug; the value in location.hash is wrong, but if you click reload or go back then forward again, the right page/hash shows up. My theory is that this is caused by the page being still in the session history / fastback cache (new with 1.5), but I haven’t turned it off to test it completely. (If after step 3 you visit more than 8 pages then go back to the demo in step 4, which should flush the demo out of the cache, the right page/hash appears).

    Has anyone seen the same behaviour before? Any workaround or information (even a bugilla report)?

    by Jeffery To on October 4 2006, 05:42 #

  • Recently implemented a solution that was originally based on Brad Neuberg’s code but now has the advantages of having been cleaned up a bit and, best of all, no longer requires a separate iframe. This means that IE will not hit the webserver with a request every time the back button is clicked. The code can be found here:
    http://cvs.horde.org/dimp/js/src/dhtmlHistory.js

    Usage is as simple as (in an onload function):
    if (dhtmlHistory.initialize()) {
    dhtmlHistory.addListener([insert function here]);
    }

    Code does require Prototype 1.5.1+.

    by Michael on March 20 2007, 02:31 #

  • Hello,
    your abstract is really good and above all very easy to understand (even for me with only litte js-knowledge).

    My aim is it to add the following way of opening the adress: index.php#hash=site1 instead of the id: index.php#hash=1
    This is particularly important for me (administrating the side, therefore i do not need to know all the numbers).
    Do you have an idea, how to implement this very easy?

    Today, i’ve prepared the pageholding.class to load the sites out of a mysql-db. It’s so easy now to adminstrate my site :). Thank you!!!

    by Phoenix on October 10 2006, 14:09 #

  • This works in Safari 2

    by David Jackson on October 13 2006, 10:25 #

  • :)
    this article is really commented!!!!
    very nice….
    the example, the comments, the community….
    the point is…
    i wonder
    can browser applications written in ajax and some server side script replace the standalone applications made in vb or c#
    thanx

    by sven on October 30 2006, 08:57 #

  • I think both Mike’s and my solution needs a rewrite. Both seem to be incompatible with the latest Firefox and Opera.

    by Nobody on November 9 2006, 19:31 #

  • Nice code, i will try to implement in my website. But your demo page does not work at all in Opera.

    by william on December 17 2006, 04:47 #

  • Here’s the fix for Opera.

    http://www.quirksmode.org/bugreports/archives/2004/11/load_and_unload.html
    (comment 15)
    http://bloomd.home.mchsi.com/opera-onload.html

    Tested in Opera 9.

    by Owen Maule on December 19 2006, 18:01 #

  • Did anyone notice that IE DOES in fact create a new history entry if the # part of the URL is changed to an anchor tag that actually exists? One solution to the IE problem (as opposed to the hidden IFRAME) is to have a container div that you inject an anchor tag into just before changing location.hash. Here’s an example:

    <script>
    function handleClick() {
    var newAnchor;
    // Do some Ajax type thing that sets newAnchor.

    $(‘container’).innerHTML = “”;
    location.hash = newAnchor;
    }
    </script>

    <div id=”container”></div>

    <a href=”#” onclick=”handleClick(); return false;” />

    by Ben Mills on December 20 2006, 14:30 #

  • Regarding Safari, bookmarking does work, the back button doesn’t. The back button is fixed in the Safari nightly builds, so the fix will show up in a future version of Safari.

    by Duncan on November 13 2006, 16:30 #

  • @Ben:

    I noticed that too. I used it to my favour by setting the value of “document.documentElement.id” to whatever my URL fragment was going to be in the same same function as the one that changes window.location – that way there is no need to create any extra markup to handle this in IE. For example:

    function UpdateAddressBar(address) {
        document.documentElement.id = address;
        window.location.href = ”#” + address
    }

    Is what I used to get around this issue, where ‘address’ is the URL fragment identified in another part of my script.

    by Dylan Parry on January 9 2007, 09:04 #

  • As people have noted already, the example doesn’t work at all in Opera (v9.02/Win for me). Can’t open the three pages and the back button is seriously broken.

    @Matthias Willerich – Opera added XmlHttpRequest in v8.0 (http://www.opera.com/docs/changelogs/windows/800/) so yes it’s now in the major release. Initial support wasn’t perfect but seems far more robust in 9.

    by Ben Buchanan on November 14 2006, 23:04 #

  • This is all well and good but because search engine crawlers don’t execute JavaScript they will never see the correct page accessed with a # url. So this approach is not SEO friendly.

    by Matt on February 19 2007, 00:39 #

  • @Matt: Look in the source. There aren’t any href=”#” attributes – all the links work without JS. That’s the point.

    by Mike Stenhouse on February 19 2007, 06:26 #

  • This is really more of a experiment/proof-of-concept kind of thing. I believe that neither Mike nor me meant this to be used as a main means of navigation for a serious website (this one doesn’t use it, for example). We basically reinvented the wheel with Javascript here, that’s all. We proved it can be done.

    by Nobody on February 21 2007, 03:09 #

  • ak, sorry, forgot to address my previous comment to Matt. A little crarification as well: the site navigation thing is the experiment/proof-of-concept. The actual code and technology utulized here is useful for AJAX apps that don’t need a search engine or anything but that would need to save the state of an app in the back/forward buffers and into bookmarks, such as Google Maps.

    by Nobody on February 21 2007, 03:20 #

  • Michael,
    I’ve had a brief look at it, and it looks like a very nice and clean implementation, and well documented, too!

    From what I could see, the solution is mainly (only?) for Firefox and IE. I wonder if and when someone finds the time to do a nicely coded solution that contains the Safari solution as well.
    Then again, the one that I was told of, was based on a bug, so that might be fixed with the next release and “break” the back button…
    Is there a test case for this anywhere online?

    by Matthias on March 27 2007, 17:09 #

  • I fixed my script, and even though it is still a lottle bit messy, it works in all the latest versions (IE 6 and 7, FFx 2, O 9) of the current browsers. http://chudosok.info

    by Nobody on February 12 2007, 19:44 #

  • @Nobody: I think Matt’s missed that the script works unobtrusively so there actually aren’t any href=”#” in the HTML… Still, I agree with you as well – it’s probably not a good idea to use Ajax as the primary means of navigation!

    by Mike Stenhouse on February 22 2007, 12:49 #

  • Ok, I understand the usage but why??? If you are creating a TRUE “Internet application”, open the damn thing into a modal-style window with no nav buttons.

    You are not enhancing the user’s experience by using AJAX instead of an IFRAME. You are just creating more work for yourself.

    I use AJAX for internet applications. Constructed correctly, an Internet Application does not need all of this unnecessary JavaScript and hidden frame garbage. Everyone thinks that the BACK button is an AJAX hindrance; it is not. A website that uses AJAX to load content into a DIV container is just plain foolish and a waste of time. Don’t use AJAX because it is “cool and nifty”, use it only when it is the BETTER alternative.

    Use AJAX to run API style calls and “change” the layout of the page dynamically, not import HTML.

    To each their own I guess.

    by craig on June 27 2007, 14:58 #

  • Is there a way to do this without all the extra files and stuff? Is there just a javascript function that adds what page you loaded to the history or something?

    I’m working on a forum, but I can’t have this many extra scripts lying around.

    by DoubleAW on August 16 2007, 12:36 #

  • Is there a way to do this without all the extra files and stuff? Is there just a javascript function that adds what page you loaded to the history or something?

    by sharp aquos on December 19 2007, 16:34 #

  • Maybe browser manufacturers should fix the back button to make it work with our Ajax scripts, instead of us fixing our Ajax scripts to work with the browsers… ? It would make life a lot easier!

    This article is great! Thanks for putting your time and effort into fixing the back button, so we didn’t need to use our brains as much! ;)

    by Oliver Treend on August 24 2007, 16:12 #

  • Have anyone tried unFocus?

    by Solid on October 24 2007, 03:28 #

  • Maybe browser manufacturers should fix the back button to make it work with our Ajax scripts, instead of us fixing our Ajax scripts to work with the browsers… ? It would make life a lot easier!

    by sharp aquos on December 19 2007, 16:33 #

  • Is it at all possible to get this sort of functionality for Opera and Safari? Thanks for the gr8 article Mike?

    by Joseph on January 17 2008, 17:41 #

  • Great links, thanks. You’ve set this article up very well. Harry Fuecks’s stuff is excellent and well recommended (JPSpan.)

    I’ve also been having some trouble with IE7 on this, but managed to get a work around. Great Ajax functionality, much appreciated.

    Lastly, eval does have some uses, even if it is evil!

    Thanks again

    by linen on July 29 2007, 11:26 #

  • Hello,

    I am using a browser with a built in back button, it’s free and pretty decent. The name is Firefox, Google it.

    Seriously, the back button doesn’t need fixing, it’s not broken. The websites you mention need to be fixed. Each website that decides to implement ‘custom’ navigation is so arrogant in presuming that users will not miss the back button behavior that they have to provide that themselves. Otherwise, their funky navigation is just broken.

    To the websites that decide to break the back button: Try a usability test, almost all users know and use the back button, it’s big and it’s the first one on the row. Where do you go off breaking such a fundamental part of the User Experience?

    End of rant

    by Mike on July 30 2007, 10:23 #

  • You really just want to put the ajax requests on a stack and pop of the last request when the backbutton event is fired. What is wrong with that ??

    by agent_orange on January 7 2008, 23:05 #

  • @agent_orange: This article was about detecting a back button event, since the browsers don’t expose one to Javascript. What you do with that information once you have it is up to you…

    by Mike Stenhouse on January 8 2008, 10:49 #

  • Thanks very much, Matthias, I really appreciate it…

    by Tercüme bürosu on October 28 2007, 22:10 #

  • sharp, see the ‘salajax’ article mentioned above, it has a javascript library that is easy to use and handles the back button etc.

    by salajax on December 27 2007, 22:00 #

  • There is an article located here

    http://www.codeproject.com/KB/ajax/Salajax.aspx

    that uses a lot of the techniques outlined in this article but simplifies them by providing a class to do all the hard work, the library is extremely easy to use and seems to work very well.

    by salajax on December 3 2007, 23:02 #

  • Nice code, i will try to implement in my website. But your demo page does not work at all in Opera..

    by çeviri on January 16 2008, 04:06 #

  • You’re right – click then back must be one of the most fundamental things about the internet and if that can’t work then everything else (even if it can) will just not be looked at in the same light. It’s a fundamentally fundamental. You really can’t expect to go ahead and design without ensuring something so utterly basic is taken care of.

    by linen on July 29 2008, 15:15 #

  • Your demo page is broken

    by Beren on August 27 2008, 06:13 #

  • I had problems with this. I constructed a BETTER SOLUTION with a WORKING DEMO. Click on my name to see the solution.

    by blacklight on October 2 2008, 14:48 #

  • Excelent article, quite usefull. Isn\'t it easier though to simple have the loaded iframe execute a script to update it\'s parent hash? Then you only have to poll the hash, which you\'re doing in normal (non-microsoft) browsers anyway. You still have to make sure you don\'t do the same in IE and other browsers but it saves some code. So your iframe calls this script from the body (or head for that matter). Assuming you use #var=val#var2=val2 syntax for the hash. if (parent) { var query = window.location.search; var hash = query.replace(/[?&;]/gi, \"#\"); parent.location.hash = hash; } Then you only have to plug the iframe in IE and the rest works the same as always. No need for polling the iframes location or a function in the iframe.

    by Maarten on November 29 2008, 20:47 #

  • Hello there. Going through things after the relaunch, and seen that the demo was broken, due to a bug in JPSPAN. I fixed this and uploaded a new version. Please refer to the bug description on the JPSPAN sourceforge page. Also, if you folks out there are utilizing the YUI framework (which I\'m a big fan of), then it\'s worth looking into the Browser History Manager, which should do the same thing for you ...

    by Pascal Opitz on October 29 2008, 07:38 #