Matt RaibleMatt Raible is a Web Architecture Consultant specializing in open source frameworks.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Ajaxified Body

I've often wondered if it was possible to use Ajax to reload the main content of a web application without reloading the header, menu and footer. SiteMesh allows you to move these common elements to a decorator that gets wrapped around each page. Below is a diagram of how SiteMesh works.

SiteMesh

You can read the Introduction to SiteMesh article if you're interested in learning more about SiteMesh's basic features. By default, SiteMesh decorates text/html responses and ignores any other content type (e.g. image/gif). It also contains an <excludes> configuration element that allows you to turn off decoration when a URL matches a certain pattern. For example, the following allows you to disable decoration when "ajax=true" is passed in the URL.

<excludes>
    <pattern>**ajax=true</pattern>
</excludes>

To optimize the loading of an application so the common elements aren't loaded for each page, it should be possible to create an Ajaxified Body where the primary content area (PCA) of the site is loaded via Ajax rather than refreshing the whole page. The header, footer and navigation bar often stays the same from page-to-page, so it doesn't seem to make a whole lot of sense to load them each time the page loads. The image below shows the PCA (of an AppFuse Light application) as a grey square with a red border.

Ajaxified Body - AppFuse Light

Implementing an Ajaxified Body consists of the following steps:

  1. Adding SiteMesh and moving common elements to a decorator.
  2. Remove common elements from each individual page (if you're using includes).
  3. Configure SiteMesh so decoration is disabled when the requested URL contains "ajax=true".
  4. Write JavaScript that modifies all <a href=""> links (and buttons with onclick='location.href') in the PCA to have an onclick handler.
  5. The onclick handler should call a JavaScript function that loads the link's URL + ajax=true using XMLHttpRequest (XHR).
  6. Add XHR success handling to replace the PCA with the loaded content.
  7. Add XHR error handling to go to the URL normally when response.status != 200.
  8. Inspect the response HTML for <title> element and replace document.title if exists.
  9. Inspect the response HTML for <head> element and append to current if exists.
  10. Inspect the response HTML for <script> and <link> elements (JavaScript and CSS) and evaluate them if they exist.

As a proof of concept, I created a prototype using AppFuse Light (Prototype/Scriptaculous for Ajax). You can see a demo at the following URL. You can also download a patch or the source for this project.

http://demo.raibledesigns.com/ajaxifiedbody

Below are a number of things I discovered while writing this prototype:

  • The hardest part of implementing this seems to be coding the exceptions. It's possible you'll have some links with existing onclick handlers and you may have to disable "ajaxifying links" for those links.
  • A progress indicator is important or the page might load so fast that the user doesn't visually detect it changed. This can lead to a worse user experience because they don't see the flash of the blank page they're used to when a page load occurs.
  • While forms can be submitted via Ajax, there's no harm in leaving existing form behavior in place where the full site is reloaded after submitting a form.
  • If a particular page needs to change the common elements (header, menu, footer), it should be possible to do that with JavaScript after the PCA content loads.
  • If the success/error indicator is outside the PCA, it may need to be populated and displayed/hidden with JavaScript after the PCA loads.

I'm sure my implementation can be improved, but I'm also curious to see what you think of this idea. I know it's not revolutionary, but it's something I'm considering adding by default to AppFuse and AppFuse Light. Do any Ajax frameworks do something like this out-of-the-box?

Update: Thanks to everyone for the great feedback - keep it coming. I agree that adding history support is a must. I'll try to do that in the next day or two. This post has also been featured on Javalobby and Ajaxian.

Update 2: Added history support.

Posted in Java at Oct 03 2008, 02:33:09 PM MDT 19 Comments
Comments:

Very cool idea! I could definitely see a use for this in AppFuse along with some standardized JS that deals with the exceptions as well as the URL formatting (I'm a big Prototype fan, so using that framework would work well for me, but I could see others wanting the same solutions in JQuery and the like).

I suppose you could argue that is a bit difficult to debug without a tool to see the DOM in its current state along with the Get requests (such as FireBug) as the view source shows the initial page (understandably so since the request is asynchronous).

Either way, interesting approach ... have to play around a bit with it :-)

Posted by Johnny Wey on October 03, 2008 at 03:27 PM MDT #

Be very wary of passing too much pre-rendered HTML back from an XHR call, especially if you're (for example) passing it inside a JSON string along with other data.

You'll end up discovering all kinds of new and interesting ways that Antivirus / Antispam / Security utilities can muck with the server's response and cause problems. The latest generation of these utilities actually rewrites HTML content between the server and the browser, including XHR requests!

The worst part is, it'll work just fine on every test browser you try, then will fail when Johnny Random accesses the site with the very same browser -- only with Comcast's horrible free security product installed.

I'm actually right in the middle of a major site rebuild that's pushing quite a bit of the dynamic HTML rendering down to the Javascript layer and simply passing the data from the server in JSON.

Posted by Brian Landers on October 03, 2008 at 04:21 PM MDT #

Yes, AjaxAnywhere does this, and does a real good job. It supports the notion of named zones (area between <aa:zone name="listGrid"> for example) and you can specify which zone you want refreshed when making an ajax submit.

The idea and implementation is very clever and works seamlessly with JSP's. It essentially has a servlet filter that reads the zone name of the area to refresh from the request, and then after the JSP for the entire page is rendered on the server, only returns the fragment of content that falls within the zone to refresh. On the client side, it refreshes that area. So having just one zone for the entire "body" will provide results similar to yours.

We used this at work to Ajaxify our JSP based application and it worked extremely well with little effort.

Disclaimer : I am a project member of AjaxAnywhere. I didn't contribute a whole lot of though, all credit goes to Vitaliy. The project doesn't have a super active forum anymore but I view the project as complete. It is solid and just works.

Along with AjaxAnywhere, I used the code from here to do the url href fixing.

Posted by Sanjiv Jivan on October 03, 2008 at 04:54 PM MDT #

hmm, I just realized you had blogged about AjaxAnywhere a while ago so I must be missing something as you were aware of it. Does the Axaxified Body approach differ from AjaxAnwhere?

http://raibledesigns.com/rd/entry/ajaxanywhere

Posted by Sanjiv Jivan on October 04, 2008 at 04:27 AM MDT #

I tried this using WordPress (PHP) and ran into a few issues that are raised by this approach:

  1. back button functionality
  2. site bookmarkability
  3. search engine indexing?

The first two could be solved by implementing a JavaScript "history" solution [1], but I'm not sure about the third. It would be interesting to know how search engines would handle this type of setup.

Just some things to consider. Also, I know Google Chrome is in beta, but it doesn't handle the ajaxifiedbody demo very well ;)

[1] http://www.stilbuero.de/jquery/history/

Posted by Eric Martin on October 04, 2008 at 08:18 AM MDT #

@Sanjiv - I've blogged about AjaxAnywhere a couple times - including how to use it with Display Tag. I think the big difference b/w AjaxAnywhere and the solution posted here is that AjaxAnywhere only works with JSP and JSF (AFAIK) and it's somewhat invasive (you have to modify your template code). It might be possible to use AA tags in the SiteMesh decorator and accomplish the same thing.

Posted by Matt Raible on October 04, 2008 at 09:36 AM MDT #

ah yes, I remember that DisplayTag blog now. It's been a while. Sorry for the noise :)

Posted by Sanjiv Jivan on October 04, 2008 at 07:36 PM MDT #

Pretty cool. However, for many internet apps inappropriate in terms of SEO. Goolge won't index you site's content.

Posted by Lars Behnke on October 05, 2008 at 10:25 AM MDT #

@Lars - is the Google crawler JavaScript aware? If not, the application should function normally with JavaScript turned off. If crawlers are JavaScript aware, you could add some logic in ajaxifyURLs() so links aren't ajax-enabled.

Posted by Matt Raible on October 05, 2008 at 10:52 AM MDT #

Hi Matt, Seems very cool. One of the problems I found is the same with using frames (on old times): The URL on the address bar never changes, which makes it difficult to send URLs for a specific page within the site to a friend or even save it as a bookmark/favorite. Have you considered this as a possible restriction?

Posted by Rui Campos on October 06, 2008 at 05:27 AM MDT #

@Rui Check out this page. The left side menu reloads the iframe at the right side, and see how the URL changes accordingly by using anchors. That solves the history/bookmarking problem.

Posted by Jordi Hernandez on October 06, 2008 at 09:52 AM MDT #

You may find RSH helpful when resolving bookmarkability:

http://code.google.com/p/reallysimplehistory/

It's the same principle used by GMail and others.

Posted by Ignacio Coloma on October 06, 2008 at 01:48 PM MDT #

[Trackback] I uploaded a new version of Ajaxified Body to its demo site a few minutes ago. Changelog: Added RSH for history and bookmarking and changed to use Prototype and Scriptaculous from Google's CDN. I found Really Simple History easy to us...

Posted by Raible Designs on October 07, 2008 at 12:17 AM MDT #

Hey Matt, What software did you use to do your diagrams. They look good. Thanks.

Posted by Mario on October 07, 2008 at 06:38 PM MDT #

What software did you use to do your diagrams.

OmniGraffle (Mac only).

Posted by Matt Raible on October 07, 2008 at 06:55 PM MDT #

Whatever happened to SiteMesh 3? It doesn't look like it has been actively developed since 2005. Are you comfortable still using it in AppFuse?

Posted by Richard Cowin on October 09, 2008 at 03:56 AM MDT #

@Richard - I don't know what happened to SiteMesh 3. I agree that SiteMesh is no longer actively developed. The good news is there aren't a whole lot of bugs being reported, indicating that it's a mature product and doesn't really need new features or releases. I am still comfortable using it in AppFuse because I haven't had any issues with it.

Posted by Matt Raible on October 09, 2008 at 10:38 AM MDT #

Wicket? ;)

Posted by Antony Stubbs on October 09, 2008 at 11:29 AM MDT #

where i can download this cool script

Posted by Enid on October 26, 2008 at 01:22 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed