Thanks to jQuery's behavior of sending along a special HTTP header when doing ajax requests, there is a quick and easy way to allow any part of Drupal to be loaded into a div via ajax.

By checking for the x-requested-with header in an init hook, we can detect if a request is an ajax request or not. If it is, we can swap themes to one that is designed for ajax, where it is assumed the page is already complete and we are loading sections. (For example, a page.tpl.php might be as simple as just print $content;.)

Here is a mini module to do the theme switch.

function ajaxify_init() {
  global $custom_theme;
  if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
    $custom_theme = 'my_ajax_theme';
  }
}

Replace my_ajax_theme with the name of your custom ajax-compatible theme.

On the JS side, simply use any $.ajax or $.ajax-derived function, such as $.load.

Example:

<div id="ajax-div"></div>
<a class="ajaxify" href="http://example.com/about">Load about page</a>
...
<script type="text/javascript">
$(document).ready(function() {
  $('.ajaxify').click(function(event) {
     // Prevent the link from also working as a "real" hyperlink.
     event.preventDefault();
     $('#ajax-div').load(this.href);
  });
});
</script>

This example has the benefit of doing something sane even if JS is disabled.

Note that in Drupal 5, reattaching behaviors after the page is already loaded is difficult, and you may have to do without things like collapsible fieldsets and such, unless you are sufficiently skilled in JS to fix it yourself.