Thursday, September 29, 2011

Managing JavaScript in Drupal 7

Drupal 7 has introduced several new techniques that allow you far greater flexibility and control in the scripts you can have on your Drupal site's pages.

Weighted JavaScript

drupal_add_js() now allows you to add a weight value to each script you’re adding so you can completely control the order in which the script tags appear:
- JS_LIBRARY: Any libraries, settings, or jQuery plugins.
- JS_DEFAULT: Any module-layer JavaScript.
- JS_THEME: Any theme-layer JavaScript
Example:
drupal_add_js('jQuery(document).ready(function () { alert("Hello!"); });', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));

Adding JavaScript in the module's .info file

You can now add Javascript in the module's .info file if it should be added on every page. This allows Javascript to be aggregated in an optimal way, and is the preferred method of adding Javascript that most visitors will need on a typical site visit:
scripts[] = somescript.js

External JavaScript

drupal_add_js() now allows you to add external scripts.
Example:
drupal_add_js('http://example.com/example.js', 'external');

Overriding JavaScript

hook_js_alter() allows you to modify the path referenced by one of the scripts added by core or another module. An obvious example is if you want to use a newer version of jQuery than what comes with core:
function hook_js_alter(&$javascript) {
  $javascript['misc/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js'; // Swap out jQuery to use an updated version of the library
}

JavaScript Libraries

There is now a standard way of adding collections of JavaScript and CSS, such as jQuery plugins. If you have a set of JavaScript and/or CSS that could be considered a package, provide it as a library to other modules by implementing hook_library(), and include it it in your own pages either by using #attached['library'] or drupal_add_library(). This is the preferred way to deal with JavaScript and CSS which might be used by other modules.
Modules define libraries which can be added when needed. For example, system.module defines the Vertical Tabs library, which includes one js file and one css file:
function system_library() {
  ...
  // Vertical Tabs.
  $libraries['vertical-tabs'] = array(
    'title' => 'Vertical Tabs',
    'website' => 'http://drupal.org/node/323112',
    'version' => '1.0',
    'js' => array(
      'misc/vertical-tabs.js' => array(),
    ),
    'css' => array(
      'misc/vertical-tabs.css' => array(),
    ),
  );
  ...
  return $libraries;
}

The library gets added when it's needed, through a call to drupal_add_library:
function theme_vertical_tabs($variables) {
  $element = $variables['element'];
  // Add required JavaScript and Stylesheet.
  drupal_add_library('system', 'vertical-tabs');

  return '<div class="vertical-tabs-panes">' . $element['#children'] . '</div>';
}

Prefer drupal_add_library() and always use it for core JavaScript files

drupal_add_js() should not be used for JavaScript which is already included in a library (as all core JavaScript libraries are). Use drupal_add_library() instead.

Using jQuery

jQuery is now namespaced to avoid conflicts with other Javascript libraries such as Prototype. All your code that expects to use jQuery as $ should be wrapped in an outer context like so.
(function ($) {
  // All your code here
}(jQuery));

If you don't, you may see the error Uncaught TypeError: Property '$' of object [object DOMWindow] is not a function or similar.

Behaviors

Behavior handling has changed again in Drupal7, with modules now required to explicitly define their attach handler, and optionally specify a detach handler.
Instead of the settings being a global object, settings are now passed to your handlers directly, after the context.
These changes, besides namespaced jQuery, mean basic module code should go from
something like this in Drupal 6:
Drupal.behaviors.exampleModule = function (context) {
   $('.example', context).click(function () {
    $(this).next('ul').toggle('show');
  });
}
To something like this in Drupal 7:
(function ($) {

  Drupal.behaviors.exampleModule = {
    attach: function (context, settings) {
      $('.example', context).click(function () {
        $(this).next('ul').toggle('show');
      });
    }
  };

}(jQuery));

No comments:

Post a Comment