Drupal Behaviors

Written by
Published
Updated
Typical Read
9 minutes
Find your next web developer job
UI Developer (Contractor) at Phyton Advisors (Houston, TX)

Drupal behaviors provides a way to attach JavaScript functionality to elements on a page. In this article, learn about the 'behaviors' system and how to properly add JavaScript the Drupal way in D6, D7, D8, and D9.

Drupal Behaviors

tl;dr

Drupal behaviors have been around since D6. They provide an easy way to initialize JS on page loads and reinitialize on AJAX calls. The process is pretty straight-forward. You simply add your custom JS within a behavior wrapper, check out the examples below:

// Drupal behavior D8 & D9
Drupal.behaviors.behaviorName = {
  attach: function (context, settings) {
    // Your custom JavaScript goes inside this function...
  }
};

// Drupal behavior for D7
Drupal.behaviors.behaviorName = {
  attach: function(context) {
    // Your custom JavaScript goes inside this function.
  }
};

// Drupal behavior for D6
Drupal.behaviors.behaviorName = function(context) {
  // Your custom JavaScript goes inside this function.
};

Drupal Behaviors Explanation

If you’re writing JavaScript in Drupal, you should be using the Drupal.behaviors API. This ensures your JS is executed at the right times during the life cycle of a page (i.e. on page loads or after new DOM elements have been added).

What are behaviors?

In short, Drupal.behaviors is a more modular and better way to implement jQuery.ready. Unlike jQuery.ready which only runs once when the DOM is ready for manipulation, Drupal.behaviors can be run multiple times during page execution. Even better, they can be run whenever new DOM elements are inserted into the document (i.e. AJAX-driven content).

Drupal.behaviors can also override or even extend an existing behavior. So for instance, if a module behavior adds a bounce effect on all links, another module could replace that behavior with a different bounce effect.

Another added bonus of Drupal.behaviors (starting in Drupal 7), is the ability to use the drupal_add_js (PHP) or Drupal.settings.modulename (JS) and pass settings as a second parameter (the first being the context) to the behavior.

How to create a Drupal behavior.

Using Drupal.behaviors to attach your functionality requires that you register a new object with the behaviors system. This is done by adding a new object as an element on the Drupal.behaviors object.

The new object needs to have at least an attach method. Anytime Drupal.attachBehaviors is called it will iterate through all behavior objects and call their respective attach methods.

Drupal.behaviors.behaviorName = {
  attach: function (context, settings) {
    // Your custom JavaScript goes inside this function ...

    $('.example', context).click(function () {
      $(this).next('ul').toggle('show');
    });
  }
};
  1. context — contains DOM elements to act on
  2. settings — has any JavaScript settings added by Drupal/PHP

The first time that Drupal.attachBehaviors() is called, the context variable contains the document object representing the DOM, but for the rest of the calls context will hold the affected piece of HTML. This is often overlooked by developers, leading to inefficient code that either causes tricky bugs or stresses the browser.

Using a jQuery selector with “context” is a good practice because then only the given context is searched and not the entire document. This becomes more important when attaching behaviors after an AJAX request.

Don’t forget context.

When calling the attach method for all behaviors Drupal passes along a context variable. This variable contains the HTML DOM elements that you should attach your custom behaviors to. During the initial page load this will be the complete HTMLDocument; during subsequent calls this will be just the elements that are being added to the page. Using context in conjunction with any jQuery selectors in your code ensures you don’t attach duplicate behaviors to the same element. See the example below:

Drupal.behaviors.behaviorName = {
  attach: function(context, settings) {
    $(context).find('a.cool-link').on('click', function() {
      // Do something cool when the link is clicked.
    });
  }
}

Forgetting to use the context variable in your selectors is a really common mistake that can lead to inefficient code that puts undue load on the browser or make it tricky to find bugs.

Detach behaviors.

The converse to the attach method is Drupal.behaviors.behaviorName.detach. Any code in the detach method will be called whenever content is removed from the DOM. Allowing JavaScript behaviors to be detached from the element before it’s removed. This can be especially useful for expensive tasks like polling, which could continue to run in the background even if the DOM element they are working on has been removed.

Implement behavior detaching in your code by adding a detach method to your behavior object.

Drupal.behaviors.behaviorName.detach = function(context, setting, trigger) {
  // Your code here
}

Learn more by reading the Drupal.detachBehaviors documentation.

Here’s an example from Drupal core’s File module which applies a behavior to all file upload elements, and then detaches it anytime one is removed from the page.

Drupal.behaviors.fileAutoUpload = {
  attach: function (context) {
    ...
  },
  detach: function (context, setting, trigger) {
    if (trigger === 'unload') {
      $(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
    }
  }
};

When are behaviors called?

Drupal Behaviors are fired whenever attachBehaviors is called. They’re can be called multiple times on a page. For example, every time a form performs some Ajax operation, all Drupal behaviors will be executed again after page load, in order to attach any relevant JavaScript to the newly loaded elements. Other examples include:

  • After an administration overlay has been loaded into the page
  • After the AJAX Form API has submitted a form
  • When an AJAX request returns a command that modifies the HTML, such as ajax_command_replace()

Modules also fire Drupal behaviors, for instance:

  • CTools calls it after a modal has been loaded
  • Media calls it after the media browser has been loaded
  • Panels call it after in-place editing has been completed
  • Views call it after loading a new page that uses AJAX
  • Views Load More calls it after loading the next chunk of items
  • JavaScript from custom modules may call Drupal.attachBehaviors() when they add or change parts of the page

So you only want your behavior to run once?

What if you don’t want a behavior re-ran? Applying JavaScript to elements each time Drupal behaviors are executed can result in the same code being applied multiple times. To ensure that the JavaScript is applied only once, we can use the jQuery $.once() function. This function will ensure that the code inside the function is not executed if it has already been executed for the given element. Check out the example below:

Drupal.behaviors.behaviorName = {
  attach: function (context, settings) {
    $('.nav', context).once('remove-modals', function () {
      $(document).keyup(function (e) {
        if (e.keyCode == 27) {
          $('.nav', context).removeClass('js-flyout-active');
        }
      });
    });
  }
};

The above code will add a class remove-modals the first time it runs. The next time that Drupal.attachBehaviors() is called, .once() will find the class and skip.

Why use Drupal behaviors?

Most JS works by manipulating elements in the DOM. As such, JavaScript code generally does some extra work to ensure that the DOM is fully loaded, and the elements you want to interact with are available, before executing. Failure to do so can cause your custom behavior to not function as expected.

The non-Drupal equivalent is using JavaScript’s document.addEventListener("DOMContentLoaded", function(){}), or jQuery’s $(document).ready() syntax. Placing your code inside this statement will ensure that it is not executed until the DOM has finished loading.

Drupal takes this a step further using Drupal.behaviors to ensure that your JavaScript isn’t executed prematurely, and to make sure it’s executed again whenever the DOM changes so that your functionality will be applied to any new elements. This generally happens when AJAX requests add new content to the page.

Furthermore, modules or themes that dynamically update the DOM can call Drupal.attachBehaviors to ensure that any JavaScript functionality will work for those new elements as well.

Unless you have a really good reason not to, you should use the Drupal.behaviors API to attach your custom JavaScript to the page.

Drupal Behaviors in D8 & D9

Converting D7 Drupal behaviors to D8 or D9

When porting Drupal behaviors from Drupal 7 to Drupal 8 or 9, keep in mind there’s a change in the use of jQuery.once(). In Drupal 7, you’d do this in a behavior:

Drupal.behaviors.behaviorName = {
  attach: function(context, settings) {
    $('.example', context).once('example-behavior', function() {
      // Do stuff.
    });
  },
  detach: function(context, settings, trigger) {
    $('.example', context).removeOnce('example-behavior', function() {
      // Undo stuff.
    });
  }
};

In Drupal 8 and 9, however, you can’t pass in a function to jQuery.once(), as the API for that jQuery plugin has changed. It now acts like jQuery.filter(), in that it filters out any elements that have already been processed so they aren’t processed more than once, and returns a jQuery collection. So, in Drupal 8 and 9, the example would be:

Drupal.behaviors.behaviorName = {
  attach: function(context, settings) {
    $('.example', context).once('example-behavior').each(function() {
      // Do stuff.
    });
  },
  detach: function(context, settings, trigger) {
    $('.example', context).removeOnce('example-behavior').each(function() {
      // Undo stuff.
    });
  }
};

Drupal Behaviors in D7

Behavior handling was changed in Drupal 7, with modules now requiring you to explicitly define they’re attached handler, and optionally specify a detach handler.

Instead of the settings being a global object, settings are passed to your handlers directly, after the context. See the example below:

(function ($) {
  Drupal.behaviors.behaviorName = {
    attach: function (context, settings) {
      // Your custom JavaScript goes inside this function...
    }
  };
}(jQuery));

Another powerful feature of Drupal.Behaviors is the ability to pass local settings. This is a new addition to Drupal 7 and simple to use.

Converting D6 Drupal behaviors to D7

It’s pretty simple to convert D6 behaviors to D7. You simply move your custom JS inside the new attach function in D7 behaviors, see the examples below:

D6 Behavior

Drupal.behaviors.behaviorName = function(context) {
  $('#example', context).html(Drupal.settings.myvar);
};

D7 Behavior

Drupal.behaviors.behaviorName = {
  attach: function(context, settings) {
    $('#example', context).html(settings.myvar);
  }
};

Drupal Behaviors in D6

With the release of D6, the typical $(document).ready function is no longer needed and was replaced with Drupal behaviors. The Drupal.behaviors object is itself a property of the Drupal object and when we want our module to add new jQuery behaviors, we simply extend this object. See the example below:

Drupal.behaviors.behaviorName = function (context) {
  // Your custom JavaScript goes inside this function...
};

drupal.js has a $(document).ready() function which calls the Drupal.attachBehaviors function. In turn, it cycles through the Drupal.behaviors object calling every one of its properties.

Drupal Behaviors FAQ

What are Drupal behaviors?

Drupal.behaviors is a more modular and better way to implement jQuery.ready. Unlike jQuery.ready which only runs once when the DOM is ready for manipulation, Drupal.behaviors can be ran multiple times during page execution.

Why use Drupal behaviors?

Using Drupal behaviors ensures your JavaScript is executed at the right times during the life cycle of a page,  such as when the page loads, or after new DOM elements have been added via an AJAX request.

In Conclusion

The main concept that we should take into account is that Behaviors will be called first when the DOM is loaded, and may be called more times with a context representing new additions or changes to the DOM. Our code has to be crafted so it kicks in only when it is needed.

Did this article spark your curiosity to open a project and navigate through its behaviors? I hope it did. Open a page, dive through each behavior and ask yourself questions like when should this piece of code run? should it run when the DOM is ready or after some AJAX request completes? This mindset will help you to write sharp and efficient JavaScript.

Did you find Drupal Behaviors useful? Get articles in your inbox.

…and don’t worry, I hate spam as much as you. Expect to hear from me at most once a week.

Share on facebook
Share on twitter
Share on linkedin
Share on reddit
Share on facebook

Stay updated.

Get a weekly email with the latest trends in web development & SEO.

Write a guest post.

Have an interesting article or idea? Become a guest blogger & pitch me your concept today.

4 comments on “Drupal Behaviors”.

Nithin kolekar

# Sep 29, 2017

Could you please share more info/use case of Drupal.attachBehaviors (when should it be avoided or when a conflict might occur)?

Karthik Varma Y

# Jul 14, 2016

Simply superb !!! 🙂

Rajesh

# May 21, 2016

Great explanation for Drupal behaviors.

Partha

# Aug 26, 2017

I was working on Drupal Behaviors without having clear concept. But now after going through this article, the concept is crystal clear to me. Thanks for the explanation !!!

Join the conversation.

Your email address will not be published. Required fields are marked *

All comments posted on 'Drupal Behaviors' are held for moderation and only published when on topic and not rude. Get a gold star if you actually read & follow these rules.

You may write comments in Markdown. This is the best way to post any code, inline like `<div>this</div>` or multiline blocks within triple backtick fences (```) with double new lines before and after.

Want to tell me something privately, like pointing out a typo or stuff like that? Contact Me.