3

I am trying to store the state of a page in the hash. This works in IE and FF but in Chrome it does not seem to do anything

$(document).ready(function()
{
    window.onbeforeunload = savePageState;
});

function savePageState()
{
    var currentTab = _tabbing.getCurrentTab();

    var mapState = _mapAdapter.getMapState();
    window.location.hash =
        'reload=' + currentTab +
        '&mapType=' + mapState.MapType.getName() +
        '&lat=' + mapState.Latitude +
        '&long=' + mapState.Longitude +
        '&zoom=' + mapState.ZoomLevel;

    return;
}

Is there a quirk in Chrome that prevents me from updating the hash? Is there a better way to go about saving the pages state? Its only for the back button that it needs to be saved.

Ted Ballou
  • 737
  • 9
  • 15

2 Answers2

3

I'd recomend that you use the jQuery BBQ plugin, which manages the hash for you.

$.bbq.pushState({
    reload: currentTab,
    mapType: mapState.MapType.getName(),
    lat: mapState.Latitude,
    long: mapState.Longitude,
    zoom: mapState.ZoomLevel
});

BBQ handles merging the hash, so it will not overwrite other parameters. For example, #fun=yes becomes #fun=yes&reload=tab&...

Next, you can listen for changes to the hash:

$(window).bind('hashchange', function(e) {
    var params = e.getState();
    doStuff(params.lat, params.long);
});
josh3736
  • 139,160
  • 33
  • 216
  • 263
  • Be careful if you're software needs to run somewhere with legacy support. http://stackoverflow.com/questions/3722815/does-internet-explorer-support-pushstate-and-replacestate – Incognito Jan 25 '11 at 15:04
  • @user257493: That actually applies to the HTML5 `history.pushState()` that you linked to in your answer. The jQuery BBQ plugin works with IE 6+, FF 2+, Safari 3+, Chrome 4+ and Opera 9.6+, so there's not really any legacy concerns with the plugin. – josh3736 Jan 25 '11 at 15:10
  • In that case, its actually *really* cool that there's a plugin that does that. The pushState word implied to me it was using the native feature in HTML5. +1 for awesome plugin. – Incognito Jan 25 '11 at 15:33
  • The jQuery BBQ plugin does not seem to work with Chrome either, unfortunately, at least the version I am using (10.0.642.2). – Ted Ballou Jan 25 '11 at 16:06
  • @user257493: BBQ's `pushState` simply pushes serialized parameters onto the URL's hash (internally, it works some cross-browser magic on the `location` object to update the hash portion of the URL). It definitely makes working with ajax state much easier. – josh3736 Jan 25 '11 at 16:31
  • 1
    @Ted Ballou: I just tried it in Chrome 10.0.648.0 and it works fine. Try the [demo page](http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/) in your browser and see if it works (click the chicken/kebabs/etc links). If it does, there's something screwy going on in your page. – josh3736 Jan 25 '11 at 16:34
  • @josh3736 You are right. It seems like Chrome doesn't allow you to update the hash when you are in the onbeforeunload event. I changed that to just 'window.onclick' to see what would happen, and now its working as I expect it to. – Ted Ballou Jan 25 '11 at 19:24
1

While BBQ was nice, it did not entirely solve my problem. For it to work in Chrome / Safari, you need to update the hash before the onbeforeunload event. Part of the requirement however made it so that was the only real time that I could save the state of the page.

I came up with a solution, using both of the other answers here, using pushState and BBQ. I thought I would post it incase anyone else has the same issue I did.

function BackButtonState(saveStateCallback, pageLoadCallback)
{
    var executePopStateCounter = null;
    var myData = {};
    var browserCanPushState = history.pushState ? true : false;

    window.onbeforeunload = saveStateCallback;

    if (browserCanPushState)
    {
        //The version of safari this was tested on (5.0.3) uses an outdated version of
        //Webkit that has a bug where popstate is not called on the first page load.
        //This is a hacky work around until the problem is fixed.
        var agt = navigator.userAgent.toLowerCase();
        if (agt.indexOf("safari") != -1)
        {
            executePopStateCounter = setTimeout(pageLoadCallback, 500);
        };

        window.onpopstate = function(popEvent)
        {
            clearTimeout(executePopStateCounter);
            if (popEvent.state != null)
            {
                myData = popEvent.state;
            }
            pageLoadCallback();
        }
    }
    else
    {
        $(document).ready(pageLoadCallback);
    }

    this.savePageState = function(state)
    {
        if (browserCanPushState)
        {
            history.pushState(state, 'BackButtonState');
        }
        else
        {
            $.bbq.pushState(state);
        }
    }

    this.getState = function(item)
    {
        if (browserCanPushState)
        {
            return myData[item];
        }
        else
        {
            return $.bbq.getState(item);
        }
    }

    this.deserializeSortList = function(sortArrays)
    {
        var sortList = [];

        $.each(sortArrays, function(index, pair)
        {
            sortList.push([parseInt(pair[0]), parseInt(pair[1])]);
        });

        return sortList;
    }
}

To use this, you do something like:

function saveState()
{
    var myData = { bananas: 'are tasty' };
    _backButton.saveState(myData);

}

function pageSetup()
{
    //Do normal $(document).ready() stuff here
    var myOpinionOnFruit = _backButton.getState('bananas');
}

var _backButton = new BackButtonState(saveState, pageSetup);

This is not guarenteed to be bug free, and I am unhappy with the hack I had to do for Safari. I just wanted to post this before I forgot.

Kirill Osenkov
  • 8,786
  • 2
  • 33
  • 37
Ted Ballou
  • 737
  • 9
  • 15