In my last post about navigation in JavaScript-enhanced sites I discussed a common difficulty encountered by ajax web applications (even Gmail, way back when!). A website that uses ajax to load content uses JavaScript to change the state of the page. This may appear to the end user as a somewhat faster page load; doesn’t it seem as if you are changing pages when you go from your Gmail inbox to your Gmail spam folder? But since using the menu results in the page state being changed with ajax, not a traditional click-link-request-new-document-from-server-and-render-it approach, things get complicated. Web browser history does not log JavaScript events, meaning that the back and forward buttons don’t work by default in an ajax web application. Bookmarks also fail to work by default.

Now, Gmail long ago took steps to enable back and forward buttons. Most things you click on in Gmail will change the site address in the address bar. On a non-ajax site the query string would most likely change, in Gmail the hash, or token, changes. The hash part of a URL is the part following the # (pound sign). This part of the URL is normally used to allow users to navigate throughout a page, ie: “jump to content”. The hash/token is therefore admirably suited to identifying states in an ajax driven site.

But how is that accomplished? Many web developers are probably already familiar with how to do it, either by writing the JavaScript themselves, using a library, or some combination of the two. As I mentioned in my last post, however, I feel that I’ve been spoiled by free JavaScript downloads and could use some more practice. Sure, I can use other people’s scripts and modify them, but I don’t write much JavaScript on my own. I feel I should at least get a grasp of the concepts involved in enabling the back button on an ajax menu, even if I ultimately use a pre-written JavaScript plugin in the interest of added features or stability.

Enough rambling! What have I learned so far about JavaScript enhanced navigation in ajax sites? The steps needed are:

  1. Ensure a hash/token is added the URL when links that should be navigated are clicked.
  2. Monitor the URL for changes to the hash/token and load the appropriate state upon a change.

That sounds a bit easier than it should, but it isn’t really that hard (although, as you will see, it doesn’t take the navigation quite as far as it needs to go). Setting the hash/token is very simple and so far I’ve only seen two ways monitor the URL: an iFrame and SetInterval. Here is what I have done to create a usable JavaScript enhanced menu with ajax to load the pages and gracefully degrade.

The actual code to bind a custom “change” event to the window.location came from Ben Nadel, but I don’t feel like I “cheated” on my goal because I had gotten it working with regular JavaScript functions, but while researching SetInterval() for more info I found his post and realized his jQuery solution was far more elegant. I am vaguely familiar with jQuery’s ability to create and trigger custom events from seeing it in a pagination plugin I once downloaded, though I need to understand anonymous functions better. Anyway, it is a far more elegant way to write the JavaScript because, as Ben explains, it will go ahead and execute at once (which helps with the bookmarking part) and, well, it just looks nicer than my series of 8-or-so functions. You can write like it’s a normal event in jQuery doing it this way, too.

However, even the bound change event ultimately relies on SetInterval() — it’s not really an event binding, because a change in window.location just isn’t a JavaScript event; the way that I understand it, if SetInterval() weren’t checking for a change every so often, the binding wouldn’t actually detect changes to window.location (unlike a normal click binding always detects click events). You can achieve the same end without using jQuery to bind and trigger the custom event, but using jQuery this way certainly streamlines the resulting code.

Something I have done personally which I haven’t seen yet (though I doubt it’s original) is that I have chosen to translate the hash/token directly into a possible file path. If you’ve read the section of the W3 CSS specification about naming classes and ids, you’ll know that you can use special characters in CSS class and id names by escaping them with a backslash and using their hex code. The url_from_token() function translates common URL characters. This way, your hash can express “the file test1.html that is in the page folder” or “the file pages.php with the query string ?id=test” (which reminds me that I forgot to translate the equal sign, whoops, I’ll have to correct that). That function needs work aside from forgetting the equal sign, though, I think it’s inefficient…or, well, it looks inefficient.

Support

The whole thing is not production ready. (It probably won’t ever be, since it’s mostly a learning exercise.) Not only is there some tweaking to be done (and some further exploration on my part into the use of iFrames in place of/in addition to SetInterval()), but the functionality isn’t 100%. My tests (on Windows) so far show:

Safari 3.2.2 & Chrome 2.0.172.31

  • Loads content
  • Back button works
  • Forward buton works
  • Direct entry works
  • Bookmarks work

Firefox 3.0.10

  • Loads content
  • Back button works
  • Forward buton works
  • Direct entry works
  • Bookmarks work
  • Swaps in href to /

Internet Explorer 7.0.5730.13

  • Loads content
  • Back button works
  • Direct entry works
  • Bookmarks work
  • Forward button does not work

Opera 9.64

  • Loads content
  • Back button works
  • Forward button works
  • Direct entry does not work
  • Bookmarks do not work