Drupal Behaviors
Hello!!
Maybe you know about Drupal.behaviors, but if you just know this word, be aware to this article.
What is Drupal.behaviors?
Drupal.behaviors is an object inside the Javascript structure in Drupal, which allows us to attach functions to be executed at certain times during the execution time of the application.
Drupal core will call Drupal.behaviors when the DOM is fully loading, but these behaviors can be called again.
Difference between Drupal.behaviors and document.ready
The advantage of Behaviors over document.ready() or document.addEventListener(“DOMContentLoaded”, function(){}) is that they are automatically re-applied to any content loaded through AJAX.
document.ready() or document.addEventListener(“DOMContentLoaded”, function(){}) (jQuery and JS code) only run once when the DOM is ready, but Drupal.behaviors can be run multiple times during page execution.
For example, in views with infinite scroll, more elements will be loaded when user clicks load “more button”, hence, DOM will change after initial load. But Drupal will call attached behaviors when the DOM is loaded, and also, when it’s changed by Ajax.
What if we want to add classes to the newly added elements? Here Drupal Behaviors come handy. Also, these behaviors allow you to override or extend the 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.
How to use them?
Drupal official documentation says that all modules have to integrate javascript using Drupal.behaviors. The new object needs to have at least an attach method.
Inside a JS file you have to add this structure:
Drupal.behaviors.behaviorName = {
attach: function (context, settings) {
// Custom code to be run on page load and on Ajax load
$('.element-example', context).click(function () {
$(this).next('ul').toggle('show');
});
}
};
Like you can see, we have to pass two arguments: context and settings:
- Context: It is the modified DOM object, it refers to the HTML that is being affected. It allows us to act within the affected scope, for example in the submission of an Ajax form the context will only be the HTML of the form.
- Settings: contains all the settings injected server side (added by Drupal/PHP). This argument is added in Drupal 8.
It’s important to say that the first time that attach() method is called, the context variable contains the document object (the whole DOME), but for the next calls, the variable context will hold the affected piece of HTML.
Drupal behaviors started in Drupal 6, but they have changed:
// 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.
};
AttachBehaviors()
We can see this function in misc/drupal.js:
// Attach all behaviors$(function () {
Drupal.attachBehaviors(document, Drupal.settings);});
Remember that in jQuery $(function() {} ) is a shorthand for $(document).ready()
Here we see a new method: Drupal.attachBehaviors() . This method can be called more times after the DOM is loaded. For instance, Drupal core will call Drupal.attachBehaviors() when:
- When DOM is ready
- After an administration overlay has been loaded into the page
- After submitting an Ajax Form API
- When an Ajax request returns a command that modifies the HTML code, such as ajax_command_replace()
Also, contrib and custom modules can execute Drupal.attachBehaviors(). For example:
- Ctools calls it after loading a modal
- Views calls it after loading a new page using Ajax
- Views calls it when you have a pagination and you change the page
- Any custom module can be called by executing the method in JS: Drupal.attachBehaviors()
When we don’t have to use Drupal.behaviors?
In general, Drupal invites us to always use Drupal.behaviors, however there are cases where it is not convenient to use it: when we need the JS code to be executed in a single moment, like a script that defines the behavior of a hamburger menu. We will only need it to be executed when the DOM is ready and no more, because it won’t affect the HTML that we manipulate in the javascript.
Detach
The converse to the attach() method is Drupal.behaviors.behaviorName.detach. Any code in the detach() method will be called before page content is about to be 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.
Drupal.behaviors.behaviorName.detach = function(context, setting, trigger) {
// Your code here}
You can see more information about detach behaviors in this doc
jQuery Once
Drupal.behaviors can be called multiple times, but if we want to run our code only one time we have to use once().
jQuery Once ensures that something is processed only once by adding a class in a DOM element after the code has been executed.
Drupal.behaviors.behaviorName = {
attach: function(context, settings) {
$('.modal', context).once('remove-modals').each(function () {
// Do stuff.
$(document).keyup(function (e) {
if (e.keyCode == 27) {
$('.modal', context).removeClass('modal-active');
}
})
})
},
detach: function(context, settings, trigger) {
$('.modal', context).removeOnce('remove-modals').each(function() {
// Undo stuff.
});
}
};
In this example, the code will add remove-modals class the first time the function is executed. But the next time that Drupal.attachBehaviors is called, .once() will find the class and will skip it.
To sum up
Behaviors will be called first when the DOM is loaded, and they can be called more times with a context that represents new additions or changes to the DOM.
More info: