Friday, August 22nd, 2008

Emulating onhashchange without setInterval

Category: Ajax, JavaScript

IE 8 has an onhashchange event, and Ajax history / bookmark management has been a long time problem of choice for developers.

Zach Leatherman has revisited the problem and has another solution that doesn’t require setInterval to check on the location.

  1. On initialization, we load an iframe onto the page that is positioned absolutely at -500px,-500px so the user can’t see it. It is a skeleton page that only needs cross browser code to add an “onscroll” event, and to be able to calculate the scrolled position of the iframe itself. For my example, I use jQuery and the dimensions plugin to accomplish this, but it could easily be trimmed down to only the bare essentials (or ported to a different library).
  2. To add an AJAX history entry into the browser’s history under an assigned hash string, we first add a <a name="hashString">hashString</a> to the <body> tag of the iframe. Using css to increase the size of the a tag proportional to the iframe’s height, we can guarantee scrolling will happen.
  3. Then, we change the location.hash of the iframe to point to that <a> tag. This will scroll the iframe to the content, and create a new entry in the browser’s history object.
  4. Inside the iframe, we have our onscroll event that fires when the scrolling in the previous step took place. (Minor IE-related workaround: The browser’s history object is changed, but the hash property doesn’t when attempting to read it later. Instead, we find the <a> that matches up with the scrollY/pageOffsetY property inside of the iframe, and retrieve the matching hash from the <a> tag.)

The solution gives you history management and back button support in one fell swoop, with the side effect of not having bookmarkability (since the iframe is updated).

Posted by Dion Almaer at 7:16 am

3.9 rating from 29 votes


Comments feed TrackBack URI

hot, what else can i say

Comment by V1 — August 22, 2008

It immediately breaks when you resize text. I’ll stick with a timer thanks.

Comment by deanedwards — August 22, 2008

I wrote a history script a few years ago that does something similar, with bookmarking:

One fun advantage over other history scripts it has is that it can tell the index in the history, as well as the hash value. So you can have two pages in the same session with the same hash value, and be able to distinguish the two (for purposes such as cacheing scroll position, etc).

It’s really, really hacky though. It actually loads the page’s content in a position:fixed iframe. And it doesn’t work in Opera :-(.

Comment by dbloom — August 22, 2008

Hey Dean,
I have a lot of respect for the work that you’ve done, but I think that concentrating on that one point of failure misses the beauty of the approach.

This is a cross browser bookmarking script that relies on almost nothing but standard browser behavior: that linking to a #name will jump to that name on the page and create a new history entry.

Removing the text inside of the div’s created inside of the iframe would fix your problem, no?

Regards, Zach

Comment by zachleat — August 22, 2008

Also, you’d have to resize your text to gargantuan proportions to actually break this approach. But I’ll update the page with a fix.

Comment by zachleat — August 22, 2008

@Zach, sorry to be a wet blanket. If you can get it working with resized text then I might reconsider. :-)

Comment by deanedwards — August 22, 2008

@Dean, I’ll work on it later tonight when I’m not on company time. I’ll update the post on my page when I’ve got it working. It’s good to stay hydrated, just try not to get soaked.

Comment by zachleat — August 22, 2008

You could fix the text-resize issue with some CSS, I think.

a {

Since the target element is a fixed size (the size should be greater than the height of the iframe, I assume), scrolling will always happen and text size won’t matter.

Comment by quixote218 — August 24, 2008

Hey quixote and Dean,
It doesn’t even need text in the a tags inside of the i-frame. You can get the current hash from the name attribute of the currently scrolled i-frame.

I’ve updated the script on my page, if you want to check it out.


Comment by zachleat — August 24, 2008

Timer is least problematic, n most easy..
y nt use this!!
if ((‘onhashchange’ in window) || (‘onhashchange’ in document))
window.onhashchange = doSomething;

Comment by SalmanMitha — January 23, 2011

Leave a comment

You must be logged in to post a comment.