AJAX in WordPress

Note: ‘AJAX’ simply meaning ‘getting data via JavaScript’, not Jesse James Garrett’s more holistic approach.

For a project I’ve been working on I needed a way to easily grab data from the backend without reloading the page. I didn’t want to fight with any of the big AJAX plugins, as the ones I looked into seemed to have their own complicated approaches involving magic markup or pre-formatted output, or didn’t seem to work. I also wasn’t interested in spending the time to learn about writing my own plugin.

After some hunting I came across a explanation (dead; temporary cache) stolen from another site (also dead) that led me to my eventual solution.

The essence is as follows: if you want to dynamically pull in, say, search results, add the following to the top of your search.php file:

<?php
  if ('1' == $_GET['ajax']) {
?>
<ul>
<? php
    if (have_posts()) {
      while (have_posts()) : the_post();
?>
 <li id='post-<?php the_ID() ?>'><a href='<?php the_permalink() ?>'><?php the_title() ?></a></li>
<?php
      endwhile;
    } else {
?>
 <li class='empty'>No results.</li>
<?php
    }
?>
</ul>
<?php
  } else {
?>

(See also: simplified WordPress post loop)

This means that if you load /index.php?s=foo&ajax=1 you might get something like:

<ul>
 <li id='post-5'><a href='http://example.com/?p=5'>foo baz!</a></li>
 <li id='post-3'><a href='http://example.com/?p=3'>foo bar!</a></li>
</ul>

Let’s hook it up

Assume our search form’s search box has the id #s (as is standard for WordPress). With the assistance of Andreas Lagerkvist’s liveSearch jQuery plugin (also dead currently, but code here) we can get a dynamically-updated list of search results.

$("#s").liveSearch({url: 'index.php?ajax=1&s='});

Make it multi-purpose

With a slight adaptation, the above approach can be used to get various other data out. For example, if we use WordPress’ built-in wp_list_categories() function…

<div id='categories'>
 <?php wp_list_categories() ?>
 <div id='in_category'></div>
</div>

The wp_list_categories() function will give markup like the following:

<li class='categories'>Categories
<ul>
 <li class='cat-item cat-item-3'><a href='http://example.com/?cat=3' title='View all posts filed under Demo'>Demo</a></li>
 <li class='cat-item cat-item-2'><a href='http://example.com/?cat=2' title='View all posts filed under Sample'>Sample</a></li>
 <li class='cat-item cat-item-1'><a href='http://example.com/?cat=1' title='View all posts filed under Uncategorized'>Uncategorized</a></li>
</ul>
</li>

If we copy the PHP we used for our search results into the top of the category.php file, we can pull in a list of a given category’s entries:

$(".categories .cat-item a").click(function () {
  theURL = $(this).attr("href") + '?ajax=1';
  $.get(theURL), function (data) {
    $("#in_category").html(data);
    return false;
  });
});

This means that if the user clicks a link for a category, instead of going to the actual page it’ll get the link’s address, tack on our ajax parameter, and stick the resultant data into the #in_category <div>. (return false cancels the click event, thus preventing the link from clicking through.)

A complication involving nice permalinks

All the above examples have assumed that the WordPress installation is using the default permalink structure (i.e. ?p=n for posts, ?cat=n for categories, and so on). Thus we used &ajax=1, because we our pages will have already have something like ?cat=3. But this assumption breaks the moment the user switches away from the default permalink structure, because our links will now have the form /category/uncategorized. Note: the old URLs will still work, they just won’t be exposed — so our search code is unaffected.

So what can we do?

Well, the easiest solution is to simply check if the URL we’re working with has a ?:

if (undefined === $(this).attr("href").split('?')[1]) {
  // using nice permalinks
} else {
  // default permalinks
}

This works by splitting the URL string into an array, using ? as a separator. Nice permalinks won’t have a question mark, so the array will only have one element (remember, arrays are zero-indexed).

Rather than repeating this code everywhere, we can write a little function that simplifies the process:

var ajaxURL = function (url) {
  var token = (undefined === url.split('?')[1] ? '?' : '&';
  return url + token + 'ajax=1';
}

And we adapt our jQuery to:

$(".categories .cat-item a").click(function () {
  theURL = ajaxURL($(this).attr("href"));
  $.get(theURL), function (data) {
    $("#in_category").html(data);
    return false;
  });
});

Finally

This approach of listening for ajax=1 will work on probably any page type. In the project that inspired this entry, I’m using it for searching, categories, pages, and date-based archives.

written 19 January, 02010 Comments