Attaching AJAX events to non-form page elements in Drupal

Making AJAX request in Drupal using the form API is real nice and simple

Posted by on

Making AJAX request in Drupal using the form API is real nice and simple, but did you know you can also attach AJAX actions to normal page elements easlily as well? In this quick tutorial I’ll show you how to easily do just that. It also provides support for browsers that dont support Javascript.

The code below is pretty standard Drupal code. It sets up a couple of menu items, and a couple of pages. One page displays a link “Click to load” and upon clicking on that link you are taken to a page that has a list of links to some common search engines. Pretty straight forward.

 1 <?php
 2 /**
 3  * Implements hook_menu().
 4  */
 5 function ajax_example_menu() {
 6   $items = array();
 7   $items['ajax_example'] = array(
 8     'title' => 'AJAX example',
 9     'page callback' => 'ajax_axample_landing_page',
10     'access arguments' => array('access content'),
11     'type' => MENU_NORMAL_ITEM,
12   );
13   $items['ajax_example/target'] = array(
14     'title' => 'AJAX target content',
15     'page callback' => 'ajax_example_target_page',
16     'access arguments' => array('access content'),
17     'type' => MENU_CALLBACK,
18   );
19   return $items;
20 }
21 
22 /**
23  * Page callback for returning content to display on the
24  * example page.
25  */
26 function ajax_axample_landing_page() {
27   return l('Click to load', 'ajax_example/target');
28 }
29 
30 /**
31  * This is the target page, We want to load this using AJAX
32  */
33 function ajax_example_target_page() {
34   $links = array(
35     'bing' => array(
36       'title' => 'Bing',
37       'href' => 'http://www.bing.com'
38     ),
39     'gogle' => array(
40       'title' => 'Google',
41       'href' => 'http://www.google.com'
42     ),
43     'yahoo' => array(
44       'title' => 'Yahoo',
45       'href' => 'http://www.yahoo.com'
46     ),
47   );
48   return theme('links', array('links' => $links));
49 }
50 ?>

Now, what if I wanted to load the search engine links page via AJAX, and append them to the current page? It’s actually really simple, and it doesn’t involve much additional code either. Lets take a look. All I need to do is make a few changes. The first thing we want to do is alter the landing page code, which will make it look like this.

 1 <?php
 2 /**
 3  * Page callback for returning content to display on the
 4  * example page.
 5  */
 6 function ajax_axample_landing_page() {
 7   drupal_add_js('misc/ajax.js');
 8   return l(t('Click to load')  , 'ajax_example/target/nojs/',
 9     array('attributes' => array('class' => array('use-ajax')))
10   ) . '<div id="target"></div>';
11 }
12 ?>

The first thing I have done here is added misc/ajax.js to the page. This will ensure that the library is included.

Secondly I have added a /nojs/ to the end of the link URI. This is a special property that the AJAX library uses when sending requests, it will be replaced with /ajax/ when making ajax requests. This value is also passed to the page callback so we know how the request was made. We'll look at that function in a second.

You'll see I have also added a CSS class to the link. use-ajax. This tells Drupal to use ajax. When this class is added, Drupal will request the URL of the link with AJAX.

I tacked on a <div id="target"></div> after the link. This is where we want to put the results from our AJAX request.

We have to make some changes to the page callback we are requesting too. Let's now take a look those changes.

 1 <?php
 2 /**
 3  * This is the target page, We want to load this using AJAX
 4  */
 5 function ajax_example_target_page($type = 'ajax') {
 6   $links = array(
 7     'bing' => array(
 8       'title' => 'Bing',
 9       'href' => 'http://www.bing.com'
10     ),
11     'gogle' => array(
12       'title' => 'Google',
13       'href' => 'http://www.google.com'
14     ),
15     'yahoo' => array(
16       'title' => 'Yahoo',
17       'href' => 'http://www.yahoo.com'
18     ),
19   );
20 
21   $content = theme('links', array('links' => $links));
22   if ($type != 'ajax') {
23     return $content;
24   }
25 
26   $commands = array();
27   $commands[] = ajax_command_replace("#target", $content);
28   ajax_deliver(array('#type' => 'ajax', '#commands' => $commands));
29 }
30 ?>

A few things have changed here, first up the function declaration now has an optional parameter, $type = 'ajax' this parameter is passed to the funciton by the Drupal AJAX library, it's value is nojs when javascript is not available. NOTE: if you are using 'page arguments' in your menu definition, then the $type variable will be last.

I have assigned the content to the $content variable and if the request isn't AJAX based we simply just return it. This allows those browsers that dont support Javascript like screen readers etc to still view the content that is linked to.

After that you'll see that I build a commands array and add the result of ajax_command_replace('#target', $content) to it. What I am doing here is telling Drupal that I want to run the ajax replace command, '#target' is the css selector of what I want to replace and $content contains the HTML I want to replace it with.

At this point I could also add any number of the pre-defined Drupal AJAX commands to the commands array, or even some custom commands I'd defined myself. However for the purpose of this tutorial, we'll keep it simple

The last thing we do is make a call to ajax_deliver(). This renders the response as a valid JSON/AJAX response. From that point the Drupal AJAX library handles the rest.

Now when we click the link we get the Drupal AJAX spinner and then the page is updated with the list of search engines. It’s as easy as that.