Mar 9, 2017 - Spencer Leopold

Mastering AJAX in Drupal 7

One of the most powerful, and oftentimes overlooked, features in Drupal is the AJAX framework. Mastering it, or even just understanding how it works, can make your life a lot easier. You come to find that a lot of that front-end functionality you've been writing can actually be done with little to no custom javascript at all.

Most of the time when we think about AJAX in Drupal, it's in the context of forms. It took me a little over a year into working with Drupal to discover that I could use it with links and page callbacks as well. A good example of this is having a button that when clicked loads some markup and injects it into the page. Previously I would set up a custom menu route that would return some markup with drupal_json_output() and wire it up manually with javascript.

That approach works, but it can be even simpler than that. Drupal has a niftly little "use-ajax" class that you can add to your buttons and links, which means you don't even have to write any javascript to override the link behavior and ajax load the route. With this approach we'll also have to update our page callback, since returning drupal_json_output() would no longer work. Instead, we can add delivery callback => ajax_deliver to our menu route and return AJAX commands directly from our page callback.

The AJAX framework also does some additional magic for us, so we that we can handle nojs situations.

 * Implements HOOK_menu().
function custom_module_menu() {
  $items['custom_module/load_webform/nojs/%'] = array(
    'page callback' => '_custom_module_load_webform',
    'page arguments' => array(2, 3),
    'access arguments' => array('custom module access permission'),
    'file' => '',
    'type' => MENU_CALLBACK,
  $items['custom_module/load_webform/ajax/%'] = array(
    'delivery callback' => 'ajax_deliver',
  ) + $items['custom_module/load_webform/nojs/%'];
  return $items;


Now we have a route for nojs and one for ajax. We just have to make sure to point our links at the nojs path, and Drupal will handle whether or not it should use the ajax path instead.


l('AJAX Link', 'custom_module/load_webform/nojs/123', array('attributes' => array('class' => array('use-ajax'))));


All that's left is our page callback.


 * Page callback for loading webforms via AJAX.
function _custom_module_load_webform($ajax, $nid) {
  if (empty($nid) || !($node = node_load($nid))) {
  $is_ajax = $ajax === 'ajax';
  if (!$is_ajax) {
  else {
    $form = drupal_get_form("webform_client_form_{$nid}", $node);
    $markup = '<div id="webform_drawer">' . drupal_render($form) . '</div>';
    $commands = array();
    $commands[] = ajax_command_replace('#webform_drawer', $markup);
    return array(
      '#type' => 'ajax',
      '#commands' => $commands,


This page callback will check to see if the route was called over ajax, and if so return commands to replace the markup of a certain div. If it wasn't called over ajax, it will redirect to the webform node, so there's a graceful fallback just in case.


Another common situation I've run into is wanting to invoke some javascript that can't exactly be called from one of the built-in ajax commands. You can write your own custom command to call from the backend, but you'll still need to write the javascript that gets executed when that command is called. Usually these are one-off things and creating a re-usable command might be a little overkill, so I've found the easiest solution is to just trigger a custom event:

$commands[] = ajax_command_invoke('html', 'trigger', array('myCustomEvent'));

and then in your theme (or module) javascript, listen for that event and do whatever it is you need to do:

$(document).on('myCustomEvent', function() {
  // Custom handling here.


Lastly, I highly suggest reading over includes/ and misc/ajax.js. There's a lot of documentation in those files that will help you understand what's going on under the hood and take your Drupal AJAX skills to the next level.