Making Ajax Driven Websites without Server Side Scripting

As some of you may or may not know, I teach a introductory technology course at my old alma matter. I have been an adjunct professor then since 2007. Back when I was a student there I had a unix shell account so I had a nifty website that used Apache’s url-rewrite tricks, and copious amounts of PHP. It didn’t have any Ajax because it did not exist back then. Or rather it did exist, but we just called it Dynamic HTML with Java-fucking-script and it was not cool yet. The IT group that handled the shell accounts had an unwritten policy of forgetting to close them down after students graduated. I think it was mostly due to the fact that there was an account signup process to get one of these accounts, but no exist process by which they would get notified to close an account when a student graduated. Of course they made an exception for me because I abused the shit out of my account.

As an adjunct professor affiliated with the Computer Science department I felt that I needed to have some web presence. For that matter I needed a web presence hosted under my universities domain name – not a personal website. That’s the only way to be legit. So, having been stripped out of my shell account was a big blow for me. I went back to the university BOFHS, hat in hand, humbled, pleading, apologetic. I begged them to bestow a shell upon me once again, and I vowed to only use it for good and the betterment of the university. Their answer was among the lines of “LOL! No!”

Of course there was a backup plan, because I always have backup plans. I learned about backups and backup plans the hard way back in 1998 when CIH virus overwrote my BIOS. Since then I have been religious about making backups, an generally having backup/failover plans for when the shit hits the fan.

I ended up making a website on the WebDav driven, dedicated Novell NetStorage server. Everyone in the university gets an account there – it is tied to your campus wide, single-sign-in account, but few people know about it. The only problem is that while you get a public directory for hosting a personal website you don’t get any server side scripting. So the best you can do is static web-pages or client side scripting.

In may of 2008 I have designed my website using some jQuery magic. But it has been a few years now, the site became stale and I decided to overhaul it.

One of the problems of that previous design was that I was using URL’s that looked like they were designed for a server-side scripted, apache-url-rewrite driven site like this:

/?p=pagename
/?p=otherpagename
/?p=stuff

There was no server-side anything though. On each page load I would simply grab the GET request, scrape it for the value of the variable p and convert it to a real URL:

/pagename.html
/otherpagename.html
/stuff.html

Then I would use jQuery load() function to asynchronously fetch that file and load it into a div. It worked, but it was backwards. Clicking on a link forced a page reload, and then an assync request right afterwards. When I sat down to re-write it, I decided to avoid these page reloads. I also wanted URL’s that would make it clear to everyone that this is a javascript land. So I decided to format them as such:

/#!/pagename
/#!/otherpage
/#!/stuff

Yes, yes – I know. Hashbangs are the root of all evil, they break the web, yadda, yadda, yadda. Keep in mind my little website was horribly broken to begin with so I’m just making it clear how broken it is. Not to mention that I went to great lengths to make this site fully accessible without Javascript.

You see, if you go to my page with your Javascript disabled, my links will look like this:

<a href="/pagename.html" class="aj">Some Page</a>
<a href="/otherpage.html" class="aj">Other Page</a>
<a href="/stuff.html" class="aj">Stuff</a>

So you can browse the entire thing with no problems. When you do enable Javascript though, this nifty function will run on page load:

function fudge_links()
{
  $("a.aj").each( function() 
  {
    h = $(this).attr("href");			
      if(h.indexOf(".") > 0)
        $(this).attr("href", "#!/" + h.substr(0, h.indexOf(".")));
  });
}

If you can’t read jQuery-speak this will go through all the links on the page marked with class=”aj” and convert them to the hashbang format I shown you above. I can’t make the server rewrite your URL as you come in, so I rewrite all my links, dynamically.

That’s only part of the equation. The other part is to parse the hashbang links and asynchronously load content. How do you do this? Like this:

hashbang = location.hash;
hash = hashbang.substr(3);
$("#content").load(hash + ".html");

Easy-peasy, right? Well, not entirely. If this code fires as the page loads, the appropriate page will be loaded. Sadly, if you call the fudge_links function either before or after it, then any links that were on the asynchronously loaded pages will not be properly fudged. To make sure the links are properly altered, we have to modify our code like so:

hashbang = location.hash;
hash = hashbang.substr(3);
$("#content").load(hash + ".html", function() { 
  if(window[hash]) window[hash](); fudge_links();});

This works, but there is a small problem. Has links do not trigger a page reload, so you now have to capture the on-click event and run this code when the content comes in. Then you have to run it on page load for when someone accesses the link directly. Having this code in two places is less than optimal. Ideally, you would want to capture the hash change event and then fire that code. Problem is that not all browsers implement this event. Fortunately, Ben Alman wrote a jQuery plugin for that aptly called Hash Change. Upon including that plugin in your page, the entire javascript logic driving the dynamic hashbang links will look like this:

$(document).ready(function() 
{
  $(window).hashchange( function(){
    hashbang = location.hash;
    hash = hashbang.substr(3);
    $("#content").load(hash + ".html", function() { 
      if(window[hash]) window[hash](); fudge_links();});
  });
 
  // fire manually when someone hits the page directly	
  $(window).hashchange();
});
 
function fudge_links()
{
  $("a.aj").each( function() 
  {
    h = $(this).attr("href");			
      if(h.indexOf(".") > 0)
        $(this).attr("href", "#!/" + h.substr(0, h.indexOf(".")));
  });
}

This works like a charm. Of course there is always a random chance that someone will randomly stumble upon one of your static webpages – like pagename.html. By design that file is basically a page fragment. It has no header, no footer, and no navigation sidebars. It is just the static content that is supposed to be loaded into your page. If someone browses to it in a non-javascript browser, then that’s fine. But ideally, if javascript is available, we would want to redirect /pagename.html to /#!/pagename and load it dynamically so that it looks nice.

To do that, I made a tiny script called redirect.js and included it at the top of every page fragment. Here are the contents:

window.onload= function() {
  h = window.location.href;
 
  if(h.indexOf(".html") > 0)
  {
    f = "#!/" + h.substr(h.lastIndexOf("/")+1);
    f = f.substr(0, f.indexOf(".html"));
    window.location = "http://example.com/" + f;   
  }   
 }

Note that I had to use an absolute URL for the window.location call because if you use a relative hash link there it will not actually reload the page (because hash means same page).

You can see a live demo of this code in action here.

This entry was posted in programming and tagged . Bookmark the permalink.



2 Responses to Making Ajax Driven Websites without Server Side Scripting

  1. Andrew Zimmerman Google Chrome Windows Terminalist says:

    Good work sir. Your webpage loads phenomenally.
    I appreciate the rundown of your AJAX functions as I do not know much about it. Nifty design- nice and minimal.

    Reply  |  Quote
  2. Luke Maciak UNITED STATES Mozilla Firefox Windows Terminalist says:

    Thank you sir. I’m generally a fan of minimal designs. That and I’m not very good at making anything good looking in Photoshop. :P

    Reply  |  Quote

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>