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 | 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
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 #
by Pascal Opitz on June 15 2005, 18:40 #
by Mike Stenhouse on June 15 2005, 18:46 #
by Eric Meyer on June 15 2005, 19:18 #
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 #
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 #
by Jonathan on June 16 2005, 09:40 #
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 #
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 #
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 #
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 #
(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 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 #
by Sam Kaufman on June 17 2005, 02:38 #
So thanks again, and nice site! It’s going into the RSS reader pronto.
by Jon Buda on June 17 2005, 08:15 #
j00 r0x duD3!
by A visitor on June 17 2005, 14:21 #
eval() is evil!
DON’T USE IT EVER… IT’S A LAME SOLUTION!
by another visitor on June 17 2005, 14:54 #
by Mike Stenhouse on June 17 2005, 15:09 #
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 #
by Matthias on June 18 2005, 23:34 #
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 #
by Pascal Opitz on June 20 2005, 02:49 #
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 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 #
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 #
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 #
by Ignacio on June 23 2005, 17:53 #
by Mike Stenhouse on June 23 2005, 20:41 #
by Vert on June 28 2005, 09:26 #
by Mike Stenhouse on June 29 2005, 02:22 #
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 #
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 #
by hao2lian on July 2 2005, 04:23 #
by Mr Spooner on July 8 2005, 16:39 #
Any comments or suggestions?
by Nobody on July 9 2005, 06:55 #
by jw on July 16 2005, 03:51 #
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 #
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 #
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 #
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 #
by Mike Stenhouse on July 25 2005, 17:00 #
by tester on July 27 2005, 18:38 #
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 #
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 #
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 #
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 #
by Nobody on July 31 2005, 04:07 #
by Nobody on July 31 2005, 04:23 #
by Nobody on July 31 2005, 04:25 #
by Mike Stenhouse on August 1 2005, 15:28 #
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 #
God Bless.
by Nobody on August 2 2005, 02:25 #
http://www.chudosok.info/commented/
by Nobody on August 2 2005, 03:43 #
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 #
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 #
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 #
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 #
by Xavier on August 29 2005, 09:06 #
Mine or Mike’s?
by Nobody on August 30 2005, 18:29 #
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 #
Brilliantly simple. Genius, even. Thanks for the great article!
by James on October 20 2005, 05:20 #
by Gavin on October 23 2005, 20:25 #
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 #
by Mike Stenhouse on October 27 2005, 06:41 #
by Mike Stenhouse on October 27 2005, 08:42 #
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 #
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 #
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 #
Interesting reading. Thanks for the link.
– Thanks!
by Jim on October 28 2005, 17:36 #
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 #
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 #
by Coder2000 on November 30 2005, 16:43 #
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 #
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 #
by repeater on December 5 2005, 19:19 #
[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 #
just a brazilian newbie.
by Juan Hafliger on December 11 2005, 06:33 #
by Udi Falkson on December 22 2005, 17:26 #
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 #
by venkat on December 21 2005, 08:21 #
by Jeff P on December 29 2005, 03:49 #
by danux on December 29 2005, 08:39 #
check erik.eae.net/playground/hashlistener.zip for the solution
by Tastaturbeschmutzer on January 9 2006, 17:06 #
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 #
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 #
by Gareth on February 6 2006, 17:25 #
by Nobody on February 6 2006, 21:24 #
by Mike Stenhouse on February 7 2006, 06:47 #
Thanks.
by b on February 7 2006, 18:55 #
by Matthias on February 8 2006, 05:16 #
by b on February 8 2006, 12:02 #
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 #
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 #
by Apoorv S on February 11 2006, 05:27 #
by Precious Edokpolo on February 17 2006, 00:19 #
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 #
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 #
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 #
by Phoenix on January 29 2006, 07:28 #
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 #
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 #
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 #
i’ll try to learn from it, my website is in need of a liftup :)
by Ben borges on April 9 2006, 12:48 #
Please!!!
by david nguyen on April 18 2006, 00:00 #
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 #
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 #
by Shadowhand on May 2 2006, 13:10 #
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 #
by bubblez on May 30 2006, 02:11 #
by Conn Buckley on June 14 2006, 17:45 #
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 #
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 #
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 #
by Mike Stenhouse on September 13 2006, 11:34 #
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 #
by Amit Aher on July 27 2006, 09:59 #
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 #
by Peter on September 13 2006, 11:23 #
by Jon Roig on August 1 2006, 21:32 #
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 #
by Mike Stenhouse on August 29 2006, 10:23 #
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 #
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 #
by Peter on September 14 2006, 09:19 #
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 #
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 #
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 #
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 #
by Nobody on November 9 2006, 19:31 #
by william on December 17 2006, 04:47 #
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 #
<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 #
by Duncan on November 13 2006, 16:30 #
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 #
@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 #
by Matt on February 19 2007, 00:39 #
by Mike Stenhouse on February 19 2007, 06:26 #
by Nobody on February 21 2007, 03:09 #
by Nobody on February 21 2007, 03:20 #
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 #
by Nobody on February 12 2007, 19:44 #
by Mike Stenhouse on February 22 2007, 12:49 #
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 #
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 #
by sharp aquos on December 19 2007, 16:34 #
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 #
by Solid on October 24 2007, 03:28 #
by sharp aquos on December 19 2007, 16:33 #
by Joseph on January 17 2008, 17:41 #
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 #
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 #
by agent_orange on January 7 2008, 23:05 #
by Mike Stenhouse on January 8 2008, 10:49 #
by Tercüme bürosu on October 28 2007, 22:10 #
by salajax on December 27 2007, 22:00 #
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 #
by çeviri on January 16 2008, 04:06 #
by linen on July 29 2008, 15:15 #
by Beren on August 27 2008, 06:13 #
by blacklight on October 2 2008, 14:48 #
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 #