UPDATED: 2008-10-02 Working Demo, and refined solution to the AJAX Back Button Fix is now available at zedwood.com.
So your back button is broken in your AJAX/PHP web app. I made a fix using PHP and HTML frames.
Flash developers have had to deal with this issue for a while, and so my fix is a variant. View Robert Penner's flash fix.
Lets say you are at a regular website with 5 pages total. After a user navigates from page1 to page2 he may want to click BACK to change the state of the website from page2 back to page1. Well in AJAX your website is no longer divided up into pages, but you must still use the idea of states that intuitively appears to the user as a different page.
So each AJAX page state is arrived at by executing a javascript function. When you click back, you'll want to execute a previous AJAX function which returns the site to the previous page state. This is accomplished with frames, but this means that if a browser is not enabled to use frames (lynx), the back button will not work. This is okay because no one would use lynx for your website anyway.
No what we have a frame overlay page, with two frames, one sized at 100%,100% and the other as invisible. When we hit a link that we want to store in the page history, we make change the page of the invisible frame to a new page, and pass it a parameter of the real page we wanted to go to. Then in our invisible frame we use that parameter to determine which ajax state to go to in the main page, which we arrive at by executing a javascript function. The effect is that when you hit the back button, it jumps to a previous page in the invisible frame and executes the javascript function associated with it, which changes the page state of the main ajax page.
Allowed Links. There are 3 kinds of links available to use on your site:
1. A HREF with _target='parent', used for external links to jump out of the framed ajax page
2. A HREF with onclick="pagenav('ajaxpagestate')" use this to navigate to a new ajax page state
3. Regular A HREF, do not use, because it will boot up the link within your frame overlay
Files needed for my PROOF-OF-CONCEPT(included below):
index.html- contains 2 frames index.php and redir_ph.php(invisible)
redir_ph.php- invisible frame used for storing page history
index.php- your main ajax app page
ajax.js- contains javascript code for ajax
ajaxfunc.php- ajax access this to returns famous quotes in xml
index.html
<html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<frameset rows="*,0" frameborder="NO" border="0" framespacing="0">
<frame src="index.php?useframes=true" name="realframe" frameborder="NO">
<frame src="redir_ph.php?p=main" name="historyframe" frameborder="NO">
</frameset>
<noframes>
<body bgcolor="#FFFFFF">
<script language="JavaScript">
<!---------
window.location.href='index.php';//index.php defaults to useframes=false
//-------->
</script>
</body>
</noframes>
</html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<frameset rows="*,0" frameborder="NO" border="0" framespacing="0">
<frame src="index.php?useframes=true" name="realframe" frameborder="NO">
<frame src="redir_ph.php?p=main" name="historyframe" frameborder="NO">
</frameset>
<noframes>
<body bgcolor="#FFFFFF">
<script language="JavaScript">
<!---------
window.location.href='index.php';//index.php defaults to useframes=false
//-------->
</script>
</body>
</noframes>
</html>
ajax.js
//--ajax
function createRequestObject() {
if(navigator.appName == "Microsoft Internet Explorer")
return new ActiveXObject("Microsoft.XMLHTTP");
return new XMLHttpRequest();
}
var httpreq = createRequestObject();
function ajaxreq(quote) {
httpreq.open('get', 'ajaxfunc.php?t='+quote);
httpreq.onreadystatechange = ajaxresponse;
httpreq.send(null);
}
function ajaxresponse()
{
if(httpreq.readyState == 4)
{
var xml=httpreq.responseXML;
var node=xml.getElementsByTagName("quote")[0];
var quot=node.firstChild.nodeValue;
document.getElementById('maindiv').innerHTML=quot;
}
}
function createRequestObject() {
if(navigator.appName == "Microsoft Internet Explorer")
return new ActiveXObject("Microsoft.XMLHTTP");
return new XMLHttpRequest();
}
var httpreq = createRequestObject();
function ajaxreq(quote) {
httpreq.open('get', 'ajaxfunc.php?t='+quote);
httpreq.onreadystatechange = ajaxresponse;
httpreq.send(null);
}
function ajaxresponse()
{
if(httpreq.readyState == 4)
{
var xml=httpreq.responseXML;
var node=xml.getElementsByTagName("quote")[0];
var quot=node.firstChild.nodeValue;
document.getElementById('maindiv').innerHTML=quot;
}
}
redir_ph.php
<?php
if ( isset($_REQUEST['p']) )
$pagename= rtrim($_REQUEST['p']);
else
$pagename="";
$jscript="if (parent.realframe.pagenavdone) parent.realframe.pagenavdone('$pagename');";
?>
<html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<title><?php echo $pagename;?></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#000000" text="#FFFFFF">
<?php echo $pagename;?>
<script language="JavaScript">
<!---------
<?php echo $jscript;?>
//-------->
</script>
</body>
</html>
if ( isset($_REQUEST['p']) )
$pagename= rtrim($_REQUEST['p']);
else
$pagename="";
$jscript="if (parent.realframe.pagenavdone) parent.realframe.pagenavdone('$pagename');";
?>
<html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<title><?php echo $pagename;?></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#000000" text="#FFFFFF">
<?php echo $pagename;?>
<script language="JavaScript">
<!---------
<?php echo $jscript;?>
//-------->
</script>
</body>
</html>
ajaxfunc.php
<?php
if ( isset($_REQUEST['t']) )
$t= rtrim($_REQUEST['t']);
else
$t="";
header('Content-type: text/xml');
echo "<quote>";
if ($t=="money")
echo "I spent 90% of my money on women and drink. The rest I wasted - George Best";
else if ($t=="fire")
echo "Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life. - Terry Pratchett.";
else if ($t=="love")
echo "Love is temporary insanity curable by marriage. - Ambrose Bierce";
else if ($t=="success")
echo "If at first you don't succeed... So much for skydiving. - Henry Youngman.";
else if ($t=="hate")
echo "I am free of all prejudices. I hate everyone equally. - WC Fields";
else
echo "The first ninety minutes of a football match are the most important. - Bobby Robson";
echo "</quote>";
?>
if ( isset($_REQUEST['t']) )
$t= rtrim($_REQUEST['t']);
else
$t="";
header('Content-type: text/xml');
echo "<quote>";
if ($t=="money")
echo "I spent 90% of my money on women and drink. The rest I wasted - George Best";
else if ($t=="fire")
echo "Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life. - Terry Pratchett.";
else if ($t=="love")
echo "Love is temporary insanity curable by marriage. - Ambrose Bierce";
else if ($t=="success")
echo "If at first you don't succeed... So much for skydiving. - Henry Youngman.";
else if ($t=="hate")
echo "I am free of all prejudices. I hate everyone equally. - WC Fields";
else
echo "The first ninety minutes of a football match are the most important. - Bobby Robson";
echo "</quote>";
?>
index.php
<?php
if ( isset($_REQUEST['useframes']) )
$useframes= rtrim($_REQUEST['useframes']);
else
$useframes="false";
?>
<html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<title>My Main AJAX Page</title>
<script language="JavaScript" type="text/JavaScript" src="ajax.js"></script>
<script language="JavaScript" type="text/JavaScript">
<!---------
//--back button
var useframes=<?php echo $useframes;?>;
function pagenav(str)
{
if (useframes)
top.frames["historyframe"].location.href = "redir_ph.php?p="+str;
else
pagenavdone(str);
}
function pagenavdone(pagename)
{
if (pagename=="money_quote_state")
ajaxreq("money");
else if (pagename=="fire_quote_state")
ajaxreq("fire");
else if (pagename=="love_quote_state")
ajaxreq("love");
else if (pagename=="success_quote_state")
ajaxreq("success");
else if (pagename=="hate_quote_state")
ajaxreq("hate");
}
//-------->
</script>
</head>
<body style='font-family:Arial,Geneva,Sans-Serif;font-size:10pt'>
This is my main ajax page 3.
<br>
<br><a href='http://www.google.com' target='_parent'>
Use this type of A HREF for links external to you AJAX app
</a>
<br>
<br>Ajax Page States:
<br>
<a href='javascript:void(0);' onclick="pagenav('money_quote_state');">money</a>
| <a href='javascript:void(0);' onclick="pagenav('fire_quote_state');">fire</a>
| <a href='javascript:void(0);' onclick="pagenav('love_quote_state');">love</a>
| <a href='javascript:void(0);' onclick="pagenav('success_quote_state');">success</a>
| <a href='javascript:void(0);' onclick="pagenav('hate_quote_state');">hate</a>
<div id='maindiv' style='height:50px;border:1px #000000 solid'></div>
<br><a href='http://www.google.com'>Do not use a regular A HREF link like this</a>
<br>If you do , you it display your http://www.mysite.com/index.html in the location bar
but show the content of the site you were trying to reach.
</body>
</html>
if ( isset($_REQUEST['useframes']) )
$useframes= rtrim($_REQUEST['useframes']);
else
$useframes="false";
?>
<html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<title>My Main AJAX Page</title>
<script language="JavaScript" type="text/JavaScript" src="ajax.js"></script>
<script language="JavaScript" type="text/JavaScript">
<!---------
//--back button
var useframes=<?php echo $useframes;?>;
function pagenav(str)
{
if (useframes)
top.frames["historyframe"].location.href = "redir_ph.php?p="+str;
else
pagenavdone(str);
}
function pagenavdone(pagename)
{
if (pagename=="money_quote_state")
ajaxreq("money");
else if (pagename=="fire_quote_state")
ajaxreq("fire");
else if (pagename=="love_quote_state")
ajaxreq("love");
else if (pagename=="success_quote_state")
ajaxreq("success");
else if (pagename=="hate_quote_state")
ajaxreq("hate");
}
//-------->
</script>
</head>
<body style='font-family:Arial,Geneva,Sans-Serif;font-size:10pt'>
This is my main ajax page 3.
<br>
<br><a href='http://www.google.com' target='_parent'>
Use this type of A HREF for links external to you AJAX app
</a>
<br>
<br>Ajax Page States:
<br>
<a href='javascript:void(0);' onclick="pagenav('money_quote_state');">money</a>
| <a href='javascript:void(0);' onclick="pagenav('fire_quote_state');">fire</a>
| <a href='javascript:void(0);' onclick="pagenav('love_quote_state');">love</a>
| <a href='javascript:void(0);' onclick="pagenav('success_quote_state');">success</a>
| <a href='javascript:void(0);' onclick="pagenav('hate_quote_state');">hate</a>
<div id='maindiv' style='height:50px;border:1px #000000 solid'></div>
<br><a href='http://www.google.com'>Do not use a regular A HREF link like this</a>
<br>If you do , you it display your http://www.mysite.com/index.html in the location bar
but show the content of the site you were trying to reach.
</body>
</html>