Url with hash mark changing after partial postback

So, here’s the deal: When entering a page with an anchor or hash mark (#) in the url like

http://magnuskragelund.dk/page.aspx#hash-mark-value

doing a partail postback using an ASP.NET UpdatePanel will cause the content after the hash mark in the url to disappear, hence changing the url to something like

http://magnuskragelund.dk/page.aspx#

This odd behavoir recently gave me a very long night at work because it has a very unfortunate impact on pages using SWFAddress, a Flash/Javascript library that enables browser history and deep-linking in flash movies, through the use an anchor in the url, so in this post I’ll give you a few trics to make the url behave nicely.

As far as I’ve been able to investigate, the behavoir is seen in Internet Explorer 6/8, Chrome and Safari on both Mac and PC, but not in Firefox. Also I belive that the problem was introduced with the latest version of ASP.NET (3.5). As this bug report shows, Microsoft is aware of the problem, and will fix it in the soon-to-be realeased ASP.NET 4.0.

Not being able to wait for that release, I did some testing to find a solution (hack) and managed to come up with two ways of working around the problem, with great help from Rostislav Hristov, co-founder of Asual DZZD.

What is going on?
In order to make a partial postback using the Update Panel on an ASP.NET page, one has to embed a Script Manager on the page. This Script Manager control will render a large amount of Javascript on the page, that controls like the UpdatePanel will utilize to make the snazzy partial update of the page.

This Javascript tampers with the url’s hash value, appearently to enable history and deeplinking when using Update Panels, and for some (to me still unknown) reason it changes the url, removing everything after the hash mark.

How can it be fixed?
The easiest way to fix this, is to override the Javascript function that is causing all the balooza, namely the “Sys._Application.prototype._setState” function. All you have to do is insert af script like the one below after the Script Manager on your page

<script type=”text/javascript”>
Sys._Application.prototype._setState = function() {}
</script>

If you prefer not to tamper with “black box” of Javascript that the Script Manager adds to the page, you can make sure to add a query string parameter to the hash value, eg.

http://magnuskragelund.dk/page.aspx#hash-mark-value?x=y

This will cause the Javascript in question to act differently, and not change the url. It will result in some not-so-pretty urls though.

  1. Jamie says:

    Excellent, overriding the Javascript appears to have fixed this issue for me! However, are you aware of any side effects this could cause??

  2. magnuskragelund says:

    Hi Jamie,
    I haven’t had any problems after overriding the funtion. Looking at what we actually override

    function Sys$_Application$_setState(entry, title) {
    entry = entry || ”;
    if (entry !== this._currentEntry) {
    if (window.theForm) {
    var action = window.theForm.action;
    var hashIndex = action.indexOf(‘#’);
    window.theForm.action = ((hashIndex !== -1) ? action.substring(0, hashIndex) : action) + ‘#’ + entry;
    }

    if (this._historyFrame && this._historyPointIsNew) {
    this._ignoreIFrame = true;
    this._historyPointIsNew = false;
    var frameDoc = this._historyFrame.contentWindow.document;
    frameDoc.open(“javascript:””);
    frameDoc.write(“” + (title || document.title) +
    “parent.Sys.Application._onIFrameLoad(‘” +
    entry + “‘);”);
    frameDoc.close();
    }
    this._ignoreTimer = false;
    var currentHash = this.get_stateString();
    this._currentEntry = entry;
    if (entry !== currentHash) {
    var loc = document.location;
    if (loc.href.length – loc.hash.length + entry.length > 1024) {
    throw Error.invalidOperation(Sys.Res.urlMustBeLessThan1024chars);
    }
    if (this._isSafari2()) {
    var history = this._getHistory();
    history[window.history.length - this._historyInitialLength + 1] = entry;
    this._setHistory(history);
    this._historyLength = window.history.length + 1;
    var form = document.createElement(‘form’);
    form.method = ‘get’;
    form.action = ‘#’ + entry;
    document.appendChild(form);
    form.submit();
    document.removeChild(form);
    }
    else {
    window.location.hash = entry;
    }
    if ((typeof(title) !== ‘undefined’) && (title !== null)) {
    document.title = title;
    }
    }
    }
    }

    my guess is, that unless you are utalizing the build in history management when creating ajax applications, you want run into any trouble.

  3. Nate Rickard says:

    Wow, I’ve been debugging this one all day. I have a custom url navigation control using location.hash and this was trampling all over it. Strangely enough, this only recently became a problem for us and our ajax url framework seemed to be working fine prior to that. Thank you for the ideas on how to render their terrible code harmless.

  4. Semyon Krotkih says:

    Excellent post! It saved me hours. Thanks a lot.

  5. Ryan says:

    Thanks so much for the article. This problem has been causing me frustrations for several days now.

  1. There are no trackbacks for this post yet.

Leave a Reply