AJAX commands in Drupal 7

One of the great 'new' features of Drupal 7 is the ability to use AJAX commands outside of FORM API allowing back-end and front-end developers to leverage high performance and solid JSON responses. JSON AJAX commands can be used simply and solidly in Drupal 7 to update blocks, nodes, pages and basically any element of a page with better performance on the client and server-side then loading AHAH page fragments.

In this tutorial we're going to create a simple and lightweight module that loads some content into a predefined page. Creating a module is pretty simple stuff but if you get lost, check out my Super simple custom module tutorial.

From the beginning:
hook_menu and nojs

Start by creating a custom module and enabling it. Happy days. The next step is to go ahead and define three new menu paths in hook_menu which will be:

  1. The page that has the link that runs the JavaScript (we'll call it the 'trigger' page).
  2. The JSON response that sends the commands to the core drupal.ajax library that ends in /ajax.
  3. The Non JavaScript fallback page that ends in /nojs.

It's very important to use /nojs and /ajax somewhere in your URL no matter what you're trying to achieve as Drupal's AJAX system will automatically replace 'nojs' with 'ajax' when your JavaScript loads.

/** * Implements hook_menu(). */ function MY_CUSTOM_MODULE_menu() { // Returns AJAX commands if the user has JavaScript turned on. $items['my-custom-path/ajax'] = array( 'title' => 'My custom ajax callback', 'page callback' => 'MY_CUSTOM_MODULE_ajax_callback', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // Returns a Non-JavaScript alternative. $items['my-custom-path/nojs'] = array( 'title' => 'My custom non-javascript callback', 'page callback' => 'MY_CUSTOM_MODULE_nojs_callback', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // The page that will run the command. $items['my-custom-path/trigger-page'] = array( 'title' => 'AJAX trigger page', 'page callback' => 'MY_CUSTOM_MODULE_trigger_page', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); return $items; }

The Callbacks

Right, so hook_menu tells Drupal which function names to call when each path is accessed using the 'callback' parameter but we still need to define those functions in our module so that Drupal has something to run. This is where the magic happens...

The Callbacks:
The trigger page

Lets start with the trigger page which as you remember is the page that will contain a solitary link to execute our marvelous AJAX commands. This function needs to do 3 things and they are ...

  1. Tell Drupal to load in it's core AJAX libraries.
  2. Tell Drupal to load in our custom JavaScript (which we'll get to later on this tutorial).
  3. Present the user with a link that runs our AJAX commands and has the id my-special-link (important for later on ... trust me).

The best part is that each of those 3 things only takes one line of code to do (very exciting stuff).

function MY_CUSTOM_MODULE_trigger_page() { // Load in Drupal core AJAX library. drupal_add_library('system', 'drupal.ajax'); // Load in our custom JavaScript. drupal_add_js(drupal_get_path('module', 'MY_CUSTOM_MODULE') . '/MY_CUSTOM_MODULE.js'); // Present the user with a link with the id 'my-special-link'. return l('Run my AJAX commands', 'my-custom-path/nojs', array( 'attributes' => array( 'id' => 'my-special-link' ) )); }

The Callbacks:
The AJAX response

This is where we get down and dirty, and this is the whole reason for this tutorials existence ... ready for it? AJAX Commands! AJAX Commands are so simple it hurts however their documentation (available at http://api.drupal.org/api/drupal/includes%21ajax.inc/group/ajax_commands/7) can be a bit daunting for a newbie. In this callback we only need to do 3 things (and you guessed it, each of them is only a single line of code).

  1. Define an array to hold all our awesome AJAX commands in.
  2. Add a new AJAX command to our array that inserts the text 'Hello world' after our link (using the id my-special-link (told you we'd need that).
  3. Return our AJAX commands in (super fast) JSON.
/** * AJAX callback that prints hello world underneath our link. */ function MY_CUSTOM_MODULE_ajax_callback() { // Define a new array to hold our AJAX commands. $ajax_commands = array(); // Create a new AJAX command that replaces the #page text with our own text. $ajax_commands[] = ajax_command_after('#my-special-link', 'Hello world'); // Return our commands in JSON. ajax_deliver(array('#type' => 'ajax', '#commands' => $ajax_commands)); }

The Callbacks:
The fallback

Undeniably the most boring, some may say most important, part of this tutorial is providing a fallback for users without JavaScript enabled in their browser (and some search engines). It's not even worth explaining this one.

/** * Non-JavaScript callback that returns hello world. */ function MY_CUSTOM_MODULE_nojs_callback() { return "Hello world"; }

The JavaScript
(not too much I promise)

In Drupal JavaScript can be hard to pull of cleanly. There's a few things you need to know about jQuery scope and Drupal behaviors to full understand this part and you can read all about that in my tutorial JavaScript and jQuery the Drupal way.

Because we've defined our AJAX commands in our callback and we're using the drupal.ajax core library the JavaScript is massively reduced (and sparkly clean too). As an extra special treat I've included event settings that makes the link respond to tap events on Android and IOS (without this you'd need a double tap to 'click' the link).

If you wanted to apply Drupal.ajax to more than one link on a page you're best bet would be to target the links using a jQuery $.each() loop however for the purposes of our quick and dirty demo, we're going to be targeting a single element using an ID.

Note

You can also bypass the need for custom JavaScript all together by using the class 'use-ajax' on your link (which Drupal will pick up automatically however this will remove the ability to customise your before/success callbacks and you won't get the 'tap' event on mobile devices.

(function ($) { Drupal.behaviors.my_custom_module = { attach: function(context, settings) { // Use context to ensure the link is only ever activated if it's regenerated. var $mySpecialLink = $('#my-special-link', context); // Only run if the link exists in the current page load or fragment refresh. if ($mySpecialLink.size() > 0) { new Drupal.ajax('#my-special-link', $mySpecialLink, { url: $mySpecialLink.attr('href'), settings: {}, event: 'click tap' }); } } } })(jQuery);

Demo & Further reading

Here's a demo I prepared earlier http://interactivejunky.com/tutorial-demos/ij-ajax-commands/trigger-page using the code in this tutorial. Sure it's not particularly fancy but it's a start. There's a lot of interesting things you can do with AJAX commands.

I recently completed a website for Hoxton Square Bar and Kitchen in London that uses AJAX Commands to load in Nodes (using node_view() for those playing at home) into an Isotope grid (but does the nojs a bit differently, naughty naughty) that you can check out here http://www.hoxtonsquarebar.com/. Drupal.ajax exposes paramaters for callbacks before and after the load is complete which allowed me to trigger jQuery Isotope actions for refreshing the layout of the grid. I also created an AJAX command to change the page title according to what node is being accessed (you can read all about how to do that at http://interactivejunky.com/snippet/changing-page-title-ajax-commands-dr...).

Drupal 7's AJAX commands are practical, stable, clean and simple and I'd really like to see more developers leveraging them and creating some really cool AJAX driven Drupal sites. This tutorial is completely basic but I hope it's at least got you curious about exploring the documentation pages (http://api.drupal.org/api/drupal/includes%21ajax.inc/group/ajax_commands/7) and doing AJAX the 'Drupal way'.