Content with Style

Web Technique

Safari, Ajax and the back button

by Matthias Willerich on July 10 2006, 09:42

A while ago David Bloom posted a back button, bookmarking and so on fix for Safari, which, aside from some funny character garbage at the end, works reliably enough to be used for all your Ajax and Flash needs.

I’ve got the feeling that one reason it didn’t get its deserved attention is that he called it “Fragment identifier history”, which might be the right terminology, but doesn’t help much in the publicity department.

I didn’t post this before, as I wanted to compare and integrate his solution with Mike’s approach and the work of others. But time is of the essence and I didn’t have any so far, not even for the solution we currently use at Less Rain. This one fixes IE by using a client-side VB script, how weird is that?

So, I wonder, has anyone seen a solution that is publicly available, documented and works on Safari and possibly Opera as well? Or is it just not fashionable any more to incorporate this kind of solution?

Comments

  • What’s the betting that Dojo does it already?! Those guys are ridiculously good…

    by Mike Stenhouse on July 14 2006, 12:42 #

  • Geoff: I’ve read about the HistoryKeeper quite a while ago, (must’ve been before March?), and back then it claimed to support Safari, but actually didn’t.
    Other than that, it now looks like a nice and clean way of doing it. Do you have experience in using it?

    What I don’t like about Dojo is its size, and there seems to be quite an overhead going on when you use it. Correct me if I’m wrong, as I haven’t looked at it in a while. Ah, now I read the blogpost, and it’s actually not a solution based on Dojo, but refers to David Blooms solution I mentioned in the post.

    by Matthias on July 26 2006, 02:51 #

  • Actually, HistoryKeeper does seem to work for me (in Safari 2.0.[something] when I tried it). It does not “remember” history entries after you leave the page in Safari, but this could be fixed by caching them in a form field (that is what my script does).

    Even though my scrollTop technique is a little more complex, I’m going to stick with it in the next version of my script, because it won’t “break” when a version of Safari comes out without the history.length bug.

    by david bloom on July 26 2006, 10:07 #

  • Hi David,
    As I said, HistoryKeeper didn’t work for me on Safari the first time I heard of it, but that’s quite some time ago. Yours did it right away. What causes problems for me in your version is the ”%5B1%5D” that seems to creep up all the time.
    I haven’t analysed your code yet, is it possible to avoid it?

    by Matthias on July 28 2006, 07:01 #

  • Though some articles linked from here reference his site, Brad Neuberg (who works on Dojo) also has some more recent items on his site about approaches that work under Safari.

    It sounds like enough people are working on the problem that there’ll be a solution soon.

    by Scott Schiller on July 24 2006, 14:13 #

  • The character encoding garbage is due to the fact that I added a Backbase-style counter to the end of the URL fragment so that each fragment would be unique. This got ugly after I (probably unnecessarily) URL-encoded the fragment.

    by david bloom on July 28 2006, 10:44 #

  • Stephan,
    I dabbled a little bit with your approach. I liked its simplicity. Sadly, I couldn’t get Safari to register the changes in the location bar. It does work combined with Mike’s work, though.

    by Matthias on August 27 2006, 15:59 #

  • Hi Matthias,

    I have worked something out but i don’t know if it will be safe to use, so i need your expert opinion. Now i have tested this only on IE and Firefox, works fine. Here’s the script

    
    window.onload = initialize;
    
    function initialize() {
      // initialize RSH
      dhtmlHistory.initialize();
    
      // add ourselves as a listener for history
      // change events
      dhtmlHistory.addListener(handleHistoryChange);
      // determine our current location so we can
      // initialize ourselves at startup
      //var initialLocation = dhtmlHistory.getCurrentLocation();
      // now initialize our starting UI
      //updateUI(initialLocation, null);
      newLocation=”loaderpage.html”;
      ajaxpage(newLocation);
    }
    
    // THIS IS THE FUNCTION I ADDED TO THE TO ADD
    // THE HISTORY AND TO CALL AJAXPAGE WHICH
    // LOADS THE INDIVIDUAL PAGES ON THE FLY.
    function mylinker(newLocation,historyData) {
    dhtmlHistory.add(newLocation,historyData);
    ajaxpage(newLocation);
    //handleHistoryChange(newLocation, historyData);
    }
    
    /** A function that is called whenever the user
        presses the back or forward buttons. This
        function will be passed the newLocation,
        as well as any history data we associated
        with the location. */
    function handleHistoryChange(newLocation, historyData) {
      // use the history data to update our UI
      //updateUI(newlocation, historyData);
    
    // THIS WAS ALSO ADDED TO FIND OUT IF ON
    // CLICKING THE BACK BUTTON THERE WOULD NOT
    // BE ANY INTIAL LOCATION TO GO TO,
    // IF NOT THEN SHOW THE FIRST PAGE
    
      if(!newLocation) {
      newLocation=”loaderpage.html”;
    }
      ajaxpage(newLocation);
    }
    
    function ajaxpage(url){
    var page_request = false;
    //alert(url);
    //alert(containerid);
    if (window.XMLHttpRequest) // if Mozilla, Safari etc
    page_request = new XMLHttpRequest()
    else if (window.ActiveXObject){ // if IE
    try {
    //alert(“inside firt try”);
    page_request = new ActiveXObject(“Msxml2.XMLHTTP”)
    }
    catch (e){
    try{
    //alert(“inside second try”);
    page_request = new ActiveXObject(“Microsoft.XMLHTTP”)
    }
    catch (e){}
    }
    }
    else
    return false
    page_request.open(“GET”, url, true)
    page_request.onreadystatechange=function(){
    //page_request.open(“GET”, url, true)
    
    //alert(“befor call of loadpage”);
    loadpage(page_request)
    }
    page_request.send(null)
    }
    
    function loadpage(page_request){
    if (page_request.readyState  4 && page_request.status200){
    document.getElementById(‘contentarea’).innerHTML=page_request.responseText
    }
    }
    
    i have borrowed the bits and pieces of code from different people and put it together. I don’t know if it works on safari or not. Tell me if you can come up with something more substancial then this.

    Thanks a lot.
    Bhavin.

    by Bhavin on August 29 2006, 01:06 #

  • I’m implementing this stuff right now for kumaru.com and I think that I’ve found a simpler way.

    What I wanted is the browser to update the back and forward buttons so I started by doing it the normal html way: with anchors. Not surprisingly it works. Even in safari (1.3). After that I just tried to hide the anchors with CSS and have javascript change the anchor in the url instead of clicking on the link. It seems to works.

    I think that the following stuff demonstrates the basic principle. Now remains to add a function that checks to see if the url has changed and may be other stuff like other people have done.

    <html>
    <head>
    <script type=”text/JavaScript” language=”JavaScript”>
    function addHistory(name) {
    location.hash=name;
    }
    </script>
    </head>
    <body>
    <a name=”com1” style=”position:absolute; display:none; top:0px; left:0px;”>
    <a name=”com2” style=”position:absolute; display:none; top:0px; left:0px;”>
    <a name=”com3” style=”position:absolute; display:none; top:0px; left:0px;”>

    <a href=”#com1”>Normal anchor 1
    <a href=”#com2”>Normal anchor 2
    <a href=”#com3”>Normal anchor 3

    <div onclick=”addHistory(‘com1’)” style=”cursor:pointer;”>JS anchor 1
    <div onclick=”addHistory(‘com2’)” style=”cursor:pointer;”>JS anchor 2
    <div onclick=”addHistory(‘com3’)” style=”cursor:pointer;”>JS anchor 3

    </body>
    </html>

    by Stephan on August 1 2006, 07:21 #

  • Kevin Newman came up with a solution that (mostly) works in Safari too…

    the docs aren’t that great, but it could be enough to base some new work on:

    http://www.unfocus.com/projects/HistoryKeeper/

    by Geoff Stearns on July 25 2006, 08:19 #

  • Hi all,

    I have a problem that i have been pondering upon for a long long time and cannot get it fixed so am asking for your time and knowledge on this.

    I am using the ajax back-button fix by Mike Stenhouse. The script works fine but i need to modify the working of the script. According to the script, when a user clicks on the link which is something like this {a href=”content.php?hash=1”}Page 1 {/a}. This would fetch the content from a file pageholder.class.php, depending on the value of the hash variable in the link. But what i want to do is to load for example a html file on my own server say ../somefolder/somefile.html. Now you might be wondering why i want to do this as i can put all the the needed content in the pageholder.class.php file. But yes, the links that i want to load are dynamic links created by a .php file. Say for example you have a news column on your website and these are links when clicked reveal the full news section. Now when i click on the news link the full news content should load up in the content holder div tags. Does someone know how i can achieve this? Help is much appreciated. And oh, sorry about the braces, i had to use it instead of the usual tag braces in case it did not show up properly.

    Cheers

    by Bhavin on August 26 2006, 06:06 #

  • oh yes,
    sorry to not have put a link to the working demo.
    it’s no fun without seeing a working example so here it is

    http://www.webhostingfx.com/test/test.html

    by Bhavin on August 29 2006, 01:17 #

  • Bhavin,
    in case you were thinking of adding the url to the ajax request, read it out on the server and send it back: Don’t do it. You’d only open the door for someone replacing your filename with whatever.php, and reads out your source.
    Regarding your example: I’d think you’d have your news in a database? You could simply send the id to the server, and rewrite the pageholder.class.php to fetch the relevant data from the db and return the desired html.
    But don’t forget that like this, your news items won’t be indexed by searchengines, unless you create a fallback link.
    Altogether, I’m not sure if I would use AJAX at all in a case (without fallback anyway), where significant data is requested that’s otherwise not available.

    by Matthias on August 26 2006, 21:15 #

  • Hi Matthias,

    Thanks for the reply. Have a look at this link which loads content on the fly using ajax
    http://www.dynamicdrive.com/dynamicindex17/ajaxcontent.htm

    If you go through the coding, you will find that the links are created by a javascript function (ajaxpage) which has two parameters, one is the html file and one is the id of the div to which the fetched file content is loaded into. Only thing lacking in this is the back/forward button functionality. In Mike’s coding, the linking system works by verifying the hash code sent to the pageholder.class.php file and then getting the content.

    How would i be able to achieve back/forward button functionality using Mike’s code with the links not referencing the pageholder.class.php? I am sure that somehow it can be done, only thing is i am new to AJAX and don’t know much about javascript. I have downloaded, Mikes code, Dojo, Brad Neuberg’s code and the script from dynamic drive (although it doesn’t have back/forward functionality) to try and solve this. Currently i am working with brads theory, let’s see how far i can get. But i know it might take me probably days or weeks before i finally figure it out. Can you, or someone help me out or guide me with this?

    Also you mentioned about security risk. Can you please explain that to me futher.

    Thanks,
    Bhavin

    by Bhavin on August 27 2006, 03:32 #

  • This is really a case for either a mailinglist, such as webdesign-L or thelist@evolt.
    Or a cup of coffee, some overtime and a Book. Anyway, I’d think you know that already.

    I’m not sure which of Brad’s approaches you’re using; the link that Scott was pointing out is actually just a blog post mentioning David’s approach.
    If you want to stay with Mike’s concept, you’ll need to start digging in the JPSPAN part of his script, about here:
    var p = new pageholder(PageHolderHandler);
    this is where he instantiates the pageholder class that you want to replace. If you write your own class that covers your needs, and thread it in here, you should get there.

    I was actually planning to work on a new version of Mike’s script, focussing on AHAH (avoid the eval() by requesting the HTML that you need) and/or JSON (and ignoring the eval is evil voices). This I’d want to get to work on Safari as well. As much as I want to use David Bloom’s version, it crashes my Safari every time. The historykeeper forgets its last state, but that could be combined with that textarea thingy that Brad Neuberg offers somewhere on his site. Give me a couple of months…

    To get back to your problem: I currently don’t see an “easy” way for what you want to do, you’ll have to understand the code and the concept first. And if you insist on using JS and HTML only, without any serverside scripting, Mike’s approach would only work if you replaced JPSPAN.

    by Matthias on August 27 2006, 09:19 #

  • Don't forget to check out zedwood.com's solution to the problem. It includes a working demo in their ajax back button fix article. What do you think?

    by blacklight on November 7 2008, 05:00 #