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

Simplified WordPress post loop

The built-in WordPress theme has a lot of redundancy in the various entry-viewing templates (index.php, archives.php, category.php, search.php). Because I dislike repetition and don’t want to worry about whether I’ve kept the markup in sync across multiple files, I ended up with the following approach.

Each of the noted files is reset to the following code:

<?php get_header() ?>

<div id='content'>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
 <?php include('_post.php') ?>
<?php endwhile; else : ?>
 <?php include('_no_post.php') ?>
<?php endif; ?>
</div>

<ol id='nextprev'>
 <li id='previous'><?php next_posts_link('Previous Posts') ?></li>
 <li id='next'><?php previous_posts_link('Next Posts') ?></li>
</ol>

<?php get_footer(); ?>

_post.php contains whatever markup I’ve chosen for my posts and is a great way to do self-contained hAtom hentry classes. _no_post.php contains a ‘sorry, bub!’ kind of message.

Of course, the templates don’t always need to have exactly the same code — for example, in the search template you may want to indicate the page is different from the standard view.

…
<div id='content'>
<div id='search_note'>Search results for ‘<?php searchbox_text() ?>’</div>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
…

written 11 January, 02010 Comments

rainbow sand

Today I had a fit of inspiration and created a new blog design:

(Screen capture of rainbow sand design)

It’s currently named ‘rainbow sand’, which is a pretty terrible name — unfortunately my word-choosing ability was not similarly inspired. The result doesn’t quite mirror my mockup, due to uncertainty about how to best implement the offset fills. I may experiment with it further; we’ll see.

At time of writing, it’s WordPress-only, but I’ll be converting it for Tumblr. The slowest part is always the stylesheet, and that only needs to be done once.

Have a look at the code!

Update: The Tumblr variant of the theme is now complete.

written 5 March, 02009 Comments

ricardo theme ported to WordPress

See it on GitHub.

The process took about two hours, including updating WordPress and fixing a configuration issue. There is some slight mismatch between the two, largely due to platform differences, but I was able to simply copy and paste a good deal of the code.

Issues

  • Is there a template tag to check if next/previous pagination will be generated? I don’t want to have unnecessary markup rendered. Tumblr allows me to do this by wrapping all my pagination code in {block:Pagination}…{/block:Pagination} tags, and the next/previous links in {block:NextPage}…{NextPage}…{/block:NextPage}. WordPress only seems to provide a way to generate the links directly with next_posts_link.
  • I’m unclear on exactly how to set up archives. The instructions in the Codex seem to be a version behind.
  • Bizarrely, the page renders differently when I use local CSS, even though it’s exactly the same as the remote copy I use with Tumblr. For now the style.css file is present simply because it’s required to give metadata.

written 4 March, 02009 Comments