9

I want the JavaScript code from module X to run only after the one from module Y has run. Whats the best way to do this?

5

The JavaScript code used by Drupal 7 is the following:

Drupal.attachBehaviors = function (context, settings) {
  context = context || document;
  settings = settings || Drupal.settings;
  // Execute all of them.
  $.each(Drupal.behaviors, function () {
    if ($.isFunction(this.attach)) {
      this.attach(context, settings);
    }
  });
};

Drupal 6 uses similar code:

Drupal.attachBehaviors = function(context) {
  context = context || document;
  if (Drupal.jsEnabled) {
    // Execute all of them.
    jQuery.each(Drupal.behaviors, function() {
      this(context);
    });
  }
};

If JavaScript would guarantee that object/array properties are always traversed in the same order they are added, then the values would be passed from jQuery.each() to the function used as its second parameter in the same order they are inserted in the array passed as first parameter.
This would mean that the first behavior inserted in Drupal.behaviorsis executed first.
In this case, to allow the behaviors defined by your module to be executed first you could use a lighter weight for it. If you want they are executed before the other ones, the module weight should be set to -50; other values can work too. If you are using Drupal 7, drupal_add_js() allows to associate a weight to the JavaScript code; JavaScript code with a lighter weight appears first in the HTML output. This is not possible with Drupal 6, thought.

JavaScript doesn't guarantee that properties are traversed in the same order they are added. This means that different JavaScript implementations would present the same properties using a different order; it also means that the same order could change in different versions of the same browser.

  • I once found a page that listed the weight used by some modules. I don't remember the URL of that page, and if the page included only particular modules, or it was exhaustive. If I find that page, I will report here the link. – kiamlaluno Mar 31 '11 at 15:43
  • 1
    You can always just check in the system table - try select name, weight from system where type = 'module' order by weight ASC, name – cam8001 Mar 31 '11 at 16:10
  • Unfortunately I need my module's PHP (mymodule) to run before the other module's (vertical_tabs) PHP, but mymodule's JS to run after vertical_tab's js. Is there a purely javascript based way to weight behaviors? – cam8001 Mar 31 '11 at 16:14
  • @cam8001: I know that, but that would mean you should install all the modules, to know the lighter weight used by a module. The page I found listed the module weights, and it could be useful when you need to know which weight you should assign to your module to be executed before every module. I know, you could simply use an extremely light weight, but you would use a weight that is lighter than it is needed. – kiamlaluno Mar 31 '11 at 16:15
  • @cam8001: In that case, there is no way to accomplish what you are trying to do, except altering the order of the objects contained in Drupal.behaviors; as I reported $.each(Drupal.behaviors) lists the objects in the same order they are inserted, not basing on their identifier. – kiamlaluno Mar 31 '11 at 16:21
5

Use the Behavior Weights module.
(Please give it some testing before you use it on a production site)

The module allows to attach weights to behaviors, and then it hijacks/replaces drupal.attachBehaviors with a custom implementation, that does respect the weights.


Why not module weight instead, as suggested in the other answer?

  • module weight is quite a heavy tool. It applies to all hook implementations (php) and behaviors (js) of the module, while we are only interested in one single behavior.
  • the order of properties in a js object is not guaranteed by the spec (although it is implemented as-you-would-expect in almost all browsers).

See
Is the order of fields in a javascript object predicatble when looping through them? and
Elements order - for (… in …) loop in javascript.

  • @kiam, If I make this a module "behavior weight" ("bewgt") on drupal.org, would you join as a co-maintainer? It's probably going to be quite a small module, but probably better than let it be shipped with something else. I have a sample implementation locally for D6, seems to work. – donquixote Oct 7 '11 at 3:59
  • just done it.. you are still invited :) – donquixote Oct 7 '11 at 5:19
  • drupal.org/node/1302126 - I hope this will be in core in D8 :) – donquixote Oct 7 '11 at 6:05
  • It would be an interesting module, but I have to first update the code of some of my modules, for which I didn't write a line of code from ages. After that, I could be free for co-developing a new module. – kiamlaluno Nov 5 '11 at 21:53
  • Just saying, people are asking for use cases. drupal.org/node/1302126#comment-5911632 – donquixote May 17 '12 at 14:41
0

For Drupal 6 -

A simple solution, if this does not need to be portable is to put the JS you want to run last in to the theme template.php file. Theme drupal_add_js calls are always called after modules.

If it does need to be portable the "cleanest" solution in drupal 6 is to add a new module that has different weight than your module with the php. You could even have the two modules in the same folder, make the first module dependent on the first, and adjust the weights in the install/update hooks. None of which is necessary, just makes installation easier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.