Drupal 6: Improving AJAX usability by fading the screen and preventing the user from clicking on anything with jQuery

In this article I'll share some code that I use to gray out the screen during AJAX calls to prevent users from clicking on anything, and displays a visual cue to the user that their click request is still being processed.

In the following code, I created a hook_menu() implementation and 2 page callbacks; one to show a clickable link and the other to process the AJAX request.

<?php
/**
 * Implements hook_menu()
 */
function helper_menu() {

  $items = array();

  // define the page callback to show the clickable link
  $items['shadow-test'] = array(
    'title' => t('Shadow Test'),
    'page callback' => '_helper_page_callback_shadow_test',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );

  // define the page callback to process the AJAX request
  $items['shadow-test-ajax'] = array(
    'title' => t('Shadow Test Ajax'),
    'page callback' => '_helper_page_callback_shadow_test_ajax',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );

  return $items;

}

/**
 * Implements the page callback to show the clickable link
 */
function _helper_page_callback_shadow_test() {

  // include module css
  drupal_add_css(drupal_get_path('module', 'helper') .'/helper.css');

  // include module javascript
  drupal_add_js(drupal_get_path('module', 'helper') .'/helper.js');

  // generate shadow html
  $shadow_html = "<div id='shadow' style='background: url(" . '"' . base_path() . drupal_get_path('module', 'helper') .'/shadow.png' . '"' . ")'></div>";

  // define javascript variables to be passed to the DOM
  $js_vars = array(
    'helper' => array(
      'ajax_path' => base_path() . 'shadow-test-ajax',
      'shadow_html' => $shadow_html,
    ),
  );

  // pass variables to javascript
  drupal_add_js($js_vars, 'setting');

  // create a variable for page output
  $output = "";

  // create a clickable link
  // NOT: this link will be overwritten via jQuery
  $output .= l(
    t('Click Me'),
    $_REQUEST['q'],
    array(
      'attributes' => array(
        'class' => 'ajax_clickable',
      ),
    )
  );

  // return page output
  return $output;

}

/**
 * Implements page callback to process the AJAX request
 */
function _helper_page_callback_shadow_test_ajax() {

  // wait 2 seconds
  // NOTE: this line is just used to demo the "shadow" effect
  sleep(3);

  // return a JSON value
  $ret = new StdClass();
  $ret->status = true;
  print drupal_json($ret);

  die;

}
?>

After flushing my menu cache, and browsing to the first page callback, I see the following:

Shadow Off

I added some CSS to my module's CSS include file (helper.css), to help position the "shadow" full screen:

#shadow {
  z-index: 1000;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: none;
}

I created a 50% black transparent PNG image in PhotoShop:

50% black image

I added some jQuery to my module's javascript include file (helper.js), to add the click event to the link, and two functions to hide and show the shadow.

Drupal.behaviors.helper = function(context) {

  // find the link with the matching class, and add a click event
  $('.ajax_clickable').click(function(){

    // show shadow
    $(this).show_shadow();

    // make ajax call
    $.getJSON(
      Drupal.settings.helper.ajax_path,
      function(data) {

        // when the ajax call is done, hide the shadow
        $(this).hide_shadow();

      }
    );

    // prevent the a tag from actually going anywhere
    return false;

  });

}

;(function($) {

  // defines the function to show the shadow
  $.fn.show_shadow = function() {

    // ensure the shadow does not already exist
    if ($('body #shadow').length == 0) {

      // add shadow html, and fade it in
      $('body').append(Drupal.settings.helper.shadow_html).find('#shadow').fadeIn();

    }

  }

  // defines the function to hide the shadow
  $.fn.hide_shadow = function() {

    // fade it out, and then remove it from the dom
    $('#shadow').fadeOut('slow', function() {
      $(this).remove();
    });

  }

})(jQuery);

Now when I click on the link the page fades to black and the user cannot click on anything. After the 3 second delay (defined in the AJAX callback), the black shadow fades away.

Shadow On