Simple Drupal AJAX load with jQuery and delivery callback

Last updated on
5 March 2017

NOTE: This tutorial requires knowledge about custom module development or function altering. (Link to how to build a module.)

Introduction

Most AJAX code in the Drupal documentation focuses on traditional json/_get() applications, which require json manipulation of the information when it arrives on the client side. Using the jQuery.load() function you can load HTML from a URL that will render directly in a target element with only a single line of JavaScript code.

The real trick here is to get only the HTML you need instead of rendering the header, footer, and every other element that comes with a full page load. This is where the 'delivery callback' setting in hook_menu comes into play. The 'delivery callback' setting defaults to drupal_deliver_html_page() which returns a fully rendered HTML page. We need to define our own delivery callback function returning just the HTML we want to render via AJAX on the client side.

We need to

  • Change the markup of the links which will trigger the AJAX request
  • Add an additional container to the markup where the AJAX content should be rendered
  • Write/alter some code in a custom module.

Add the link markup and target container to your site

  1. Define a link to execute the AJAX call:
    <a class="btn" href="#" onclick="myModule_ajax_load()">Ajax Test</a>
  2. Set the element that will receive the AJAX callback:
    <div id="ajax-target">Ajax goes here!!!</div>

The custom module code

  1. Create a JavaScript function that will handle the call using jQuery.load(). The $nid in the path is just an example. It could be any variable.
    <script>
    function myModule_ajax_load() {
      jQuery("#ajax-target").load("/node/get/ajax/11");
    }
    </script>
    
  2. Register the URL that will render the HTML by implementing hook_menu():
    /**
    * Implements hook_menu().
    */
    function myModule_menu() {
      $items['node/get/ajax/%'] = array(
        'page callback' => 'myModule_ajax_get_ajax', // Render HTML.
        'page arguments' => array(3),
        'type' => MENU_CALLBACK,
        'access arguments' => array('access content'),
        'delivery callback' => 'myModule_ajax_callback',  // Magic goes here.
      );
      return $items;
    }
    
  3. Create page callback and delivery callback functions:
    function myModule_ajax_get_ajax($nid) {
      // This example loads a node and returns the teaser.
      // You can return whatever you want, including forms.
      $node = node_load($nid);
      return node_view($node, 'teaser');
    }
    
    function myModule_ajax_callback($page_callback_result) {
      // Only render content
      $content = drupal_render($page_callback_result);
    
      // Add CSS and JS files, add some markup
      $html = '<html><head><title></title>' . drupal_get_css() . drupal_get_js() . '</head><body class="jquery-ajax-load">' . $content . '</body></html>';
      print $html;
    
      // Perform end-of-request tasks.
      drupal_page_footer();
    }
    

Now lets load any page in your site with just 3 lines of code

The example above lets you render anything from your site, but some things are already there, like views, forms or webforms. So how can we get them without need to build them again.

For the start we need the function that renders any URL into a page using the menu.api, its name is menu_execute_active_handler(), this function by default returns a full page set on its first argument, but if you set the second argument to FALSE it returns just the content part of your page, no matter how or who generates it.

So now we will rebuild our myModule_ajax_get_ajax() function in the step 5 to return only the content of any page declared in your site.

1. Define a link to execute the AJAX call:
<a class="btn" href="#" onclick="myModule_ajax_load()">Ajax Test</a>

2. Set the element that will receive the AJAX callback:
<div id="ajax-target">Ajax goes here!!!</div>

3. Create the JavaScript function that will do the call, using jQuery.load(), I am using an arbitrary URL "node/11", that can be a variable, with a prefix "/get/ajax" that will trigger hook_menu:

<script>
function myModule_ajax_load() {
  jQuery("#ajax-target").load("get/ajax/11");
}
</script>

4. Create the URL that will render HTML with hook_menu():

/**
* Implementation of hook_menu().
*/
function myModule_ajax_menu() {
  $items['get/ajax/%node'] = array(
    'page callback' => 'myModule_ajax_get_ajax', // Render HTML
    'type' => MENU_CALLBACK,
    'page arguments' => array(2),
    'access arguments' => array('access content'),
    'delivery callback' => 'myModule_ajax_callback',  // Magic goes here
  );
  return $items;
}

5. Create page callback and delivery callback functions:

function myModule_ajax_get_ajax($node) {
  return node_view($node, 'teaser');
}

function myModule_ajax_callback($page_callback_result) {
   // Only render content and validates return
  $content = is_string($page_callback_result) ? $page_callback_result :   drupal_render($page_callback_result);
  $html = '' . drupal_get_css() . drupal_get_js() . '' . $content . '';
  print $html;
  drupal_page_footer(); 
}

Additional resources:

Tags