When developing custom Drupal modules or themes, it is common to write JavaScript. In this article we will look at best practices for writing clean JavaScript that integrates properly with Drupal — both server-side and in the browser.
We are used to grouping jQuery code inside the ready() method like this:
$(document).ready(function () {
// jQuery code here
});Using this method guarantees that the code will only execute after the Document Object Model (DOM) is fully loaded, and it also allows us to place our jQuery code anywhere on the page.
Drupal took inspiration from this method to provide a more powerful solution: Drupal Behaviors. The principle is the same as document.ready, except that it is managed by Drupal once the DOM is fully loaded.
Any Drupal-powered site contains the global Drupal object, which holds several sub-objects. The one we care about in this article is Drupal.behaviors, which contains the JavaScript code we want to execute once the DOM is fully loaded.
Example of a Drupal Behavior:
(function ($, Drupal) {
Drupal.behaviors.CustomBehavior = {
attach: function (context, settings) {
// jQuery code here.
});
}
};
})(jQuery, Drupal);The behaviors system is especially useful when the DOM is modified via Ajax calls without a page reload. After new elements are added to the DOM, Drupal invokes Drupal.behaviors to attach them to the newly added elements. The context object contains the part of the DOM that was just added — whether via Ajax or another method.
Consider the following code:
(function ($, Drupal) {
Drupal.behaviors.uniqueKey = {
attach:function () {
// Search the DOM for an element with class .behavior_example and logs it in the console.
$(".behavior_example").once("unique-key").each(function () {
console.log($(this));
});
}
};
}(jQuery, Drupal));Note: you must add core/jquery.once as a library dependency in your module to use it.
The problem here is that every time a new element is added to the DOM, the entire DOM is scanned for elements with the class behavior_example, leading to a performance loss.
This is where context comes in to solve the problem and produce more efficient code:
(function ($, Drupal) {
Drupal.behaviors.uniqueKey = {
attach:function (context) {
// This example solves the problem by searching only in the context which contains only the part that is being added to the DOM.
$(context).find(".behavior_example").once("unique-key").each(function () {
console.log($(this));
});
}
};
}(jQuery, Drupal));After a full page load,
contextcontains the entire DOM. After Ajax calls,contextholds only the part of the DOM that was newly loaded and added.