Drupal behaviors: A quick how to

Drupal behaviors: A quick how to

By Victor Künzig 13th März 2013 Drupal

If you’re adding JavaScript to your custom module it is very easy and tempting to simply add it like this:

jQuery(document).ready(function($){
  alert(‘hot dog flavored water’);
});

Now this code works perfectly fine but what if your JavaScript needs to be executed on page load and after an AJAX request? Imagine you have a view that uses “Views Infinite Scroll” and you want add a CSS class to every result like this:

jQuery(document).ready(function($){
  $('.view-display-id-page .views-row').addClass('fancy-pants');
});

This will work for the results that are displayed initially but for all the results that are loaded by Infinite Scroll's AJAX call the class is not added. That’s where Drupal behaviors come in handy.  The behaviors will be executed on every request including AJAX requests, so let's do the equivalent of the code above but this time using this method:

Drupal.behaviors.infiniteScrollAddClass = {
  attach: function (context, settings) {
    $('.view-display-id-page .views-row').addClass('fancy-pants');
  }
};

I admit that was quick - so here are some explanations:

  • infiniteScrollAddClass: This is your namespace and should be unique. For example, this is typically the name of your module, but it isn't mandatory.
  • context: This is actually really really cool, on page load the context will contain the entire document and after an AJAX request will have all the newly loaded elements. This way you can treat content that is loaded in via AJAX differently than others.
  • settings: This contains information passed on to JavaScript via PHP, it is similar to accessing it via Drupal.settings. For further comprehension I recommend this source.

There obviously are cases where some functionality should not be executed on every request. In such a case its great to use jQuery's .once() method. So let's say we want to give all the initially loaded results in our view an additional class, for something like this we would proceed like so:

Drupal.behaviors.infiniteScrollAddClass = {
  attach: function (context, settings) {
    // these are the elements loaded in first
    $('.view-display-id-page').once(function(){
      $(this).find('.views-row').addClass('i-was-here-first');
    });

    // everybody
    $('.view-display-id-page .views-row').addClass('fancy-pants');
  }
};

This will add the class “i-was-here-first” to all the view results present on page load, everybody else joining in via AJAX will just get the “fancy-pants” class.

So that’s a quick look at Drupal behaviors, if you haven’t used it do use it!

If you are looking for additional theoretical insight into this topic I can recommend these two sources for further reading:

Stay in touch – join our newsletter!

 

 

Kommentare

Bild des Benutzers mongolito404
mongolito404

Nice to see this kind of explanation on Drupal Planet. I'm surprised by how many developers, even those working with Drupal for years, don't known about behaviors and jQuery.once.

I wrote a similar explanation for work colleagues a while ago. I don't blog, but I found a place to publish it at https://coderwall.com/p/kd-4cg.

Bild des Benutzers Andrew Berezovsky
Andrew Berezovsky

Good explanation! You do mention the context, but you don't actually use it :)
To utilize the context you should change this line:
$('.view-display-id-page .views-row').addClass('fancy-pants');
to this:
$('.view-display-id-page .views-row', context).addClass('fancy-pants');

It doesn't really change the end-result in your case as this is just adding a class, and adding this class again and again will not change anything (except for performance).
If you don't use context then jQuery will try to add add 'fancy-pants' class to all the Views rows (including the ones that were already present on the page), and not to only the new ones. So you should let jQuery know that it should find only the new rows by using the context variable.

Thanks
AndyB

Bild des Benutzers fubhy (Sebastian Siemssen)
fubhy (Sebastia...

There is also an in-code example of Drupal jQuery bundled with Omega 4.x: http://drupalcode.org/project/omega.git/blob/HEAD:/starterkits/default/j...

Bild des Benutzers fubhy (Sebastian Siemssen)
fubhy (Sebastia...

"settings: This contains information passed on to JavaScript via PHP, it is similar to accessing it via Drupal.settings. For further comprehension I recommend this source."

Also note that this is similiar to 'context' in that it has the settings of the current context in it. So on initial page load it's equal to Drupal.settings while it may have be different during an AJAX request due to settings that were changed by the AJAX callback (they get merged with Drupal.settings and then handed to the behavior in 'settings' while the original Drupal.settings stays the same.)

Bild des Benutzers Mot Gio
Mot Gio

Hi Victor,
I used Modernizr to load my javascript file for mobile, all function inside Drupal.behaviors.myNamespace can't execute until make ajax request as you mention.

Has other way to use Drupal.behaviors with Modernizr?

Bild des Benutzers Jon Raedeke
Jon Raedeke

Having the exact same issue as Mot Gio.

Bild des Benutzers victor künzig
Victor Künzig

@Mot Gio & @Jon Raedeke Sorry for the late reply! Im afraid i can't quiet follow the issue, from what i understand though, the behaviour doesn't load until you make an ajax call? Would you mind posting a Code example? The whole behaviour would be nice (If its too big put it on: http://jsfiddle.net/)

Bild des Benutzers Yoke Lee
Yoke Lee

Hi - stumbled upon this page during my journey understanding Drupal.behaviour.
Could you explain a bit further on how PHP call this Drupal.behaviour?
I have a bug where "settings" is undefined in Drupal.behaviour.password that checks password strength...

p.s. I like this blog design~

Bild des Benutzers wardwell
wardwell

Hey Great Post,

@Yoke Lee -- I ran into an issue with a object within the Drupal.settings object not being defined. If you are adding your settings object in a drupal_add_js make sure that it is in a place that is getting called during drupal's bootstrap process (in other words not in a preprocess page function oslt). Adding the settings in a hook_init or a hook_boot would work.

Bild des Benutzers Renee
Renee

One thing to note, @Yoke Lee, make sure you're spelling it the US way -- behavior, not behaviour. ;)

Bild des Benutzers Ravindra Singh
Ravindra Singh

Drupal.behaviors are similar to document.ready but Drupal make sure that on every ajax call code written in a Drupal.behavior is executed again http://www.w3web.in/Understanding-of-Drupal-behaviors

Kommentar hinzufügen

Suchformular

Neue Kommentare