Working with JavaScript, jQuery and DOM Ready in Drupal 7

As compared to Drupal 6, using Drupal 7 you can no longer rely on $() as the jQuery function because it’s simply not recognized. It should be taken into account in order to ensure that there are no conflicts between JavaScript libraries that are possibly used on a site. You can face a situation when the scripts work properly during the page making or, for example, when code snippet were copied and pasted from an external source, but during the integration, troubles begin. Most likely a problem lies in use of $ as it’s no longer an alias of JQuery. If you want to avoid conflicts while including some jQuery, a good way out is wrapping the snippets in immediately invoked the anonymous function that will alias the jQuery namespace to $ as it is illustrated in the following example:

    (functon($){
        // Here $ is the jQuery namespace.
    })(jQuery);

JavaScript codes running on page load should be also wrapped in a function passed as argument to jQuery or jQuery(document).ready. It makes it possible for everything inside to load as soon as the DOM (Document Object Model) is loaded and before the page contents are loaded:

    $(functon(){
        // Code here is executed when the DOM is loaded.
    });

When these two patterns are combined, everything goes fine. Still, if content is added to the page after page load the added elements will never be processed. Similarly, if the content is partially removed, the code, for instance, will not be able to update information about the already processed elements. But in Drupal there’s an API used for such purposes and it is called behavior.

Drupal Behaviors constitute the object in JavaScript that Drupal can initialize after page load, and after every AJAX call in DOM and the latter is a huge advantage of behaviors. They enable us to facilitate the workflow and make integration of JavaScript in Drupal projects easier. Though behavior is not obligatory, it’s highly preferable in order to avoid future possible problems like the issues when code written some time ago starts behaving strangely when a contrib module is added to the project. It’s written in the following way:

    Drupal.behaviors.behaviorsName = {
        attach: function (context, settings) {
            //Do something.
        },
        detach: function (context, settings, trigger) {
            // Undo something.
        }
    };

Note that when behavior should be added to elements the attach function of all registered behaviors will be invoked. It occurs both when the DOM is ready (page load) and when elements are added to the DOM. Alongside attach there’s the detach function, which can be used to detach registered behaviors from page elements. It can happen before the elements are moved or removed from the DOM or a form is submitted. As for the context parameter, it’s a parent of the added elements. The settings parameters in their turn are the settings for the context, as a rule, the Drupal.settings object as set by calls to drupal_add_js() from PHP. Besides context and settings, the detach function also expects a trigger. It’s essentially a string containing the causing of the behavior to be detached. Possible causings can be the following:

  • unload (the context element is being removed from the DOM);
  • move (the element is about to be moved within the DOM);
  • serialize (when an Ajax form is submitted, this is called with the form as the context).

The attach and detach functions of a behavior can be used multiple times. Your JavaScript is executing well from the first sight, but sometimes the same element is processed two times or more. But Drupal 7 fix this issue with jQuery ‘Once’ plugin integrated into Drupal 7 core to add a simple class to the HTML element to ensure that the behavior is only processed one time:

    $(selector).once('behavior-name').doSomething();
    $(selector).once('behavior-name', function(){ /*do something*/});

Since a behavior is being attached and detached to and from a context, the context object can be used to restrict your jQuery queries to only the affected element or DOM subtree, like here:

    $(selector, context).doSomething();

Taking into account everything mentioned earlier, it follows that the base pattern to process elements on page load will look like this:

    (function($){
        Drupal.behavior.doSomething = {
            attach: function(context, settings) {
                $('div.something', context).once('do-something').doSomething({
                    param1: settings.something1.param,
                    param2: 'something else'
                });
            }
        }
    })(jQuery);