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?

3 Answers 3

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.

6
  • 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.
    – apaderno
    Mar 31, 2011 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, 2011 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, 2011 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.
    – apaderno
    Mar 31, 2011 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.
    – apaderno
    Mar 31, 2011 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.

5
  • @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, 2011 at 3:59
  • just done it.. you are still invited :)
    – donquixote
    Oct 7, 2011 at 5:19
  • drupal.org/node/1302126 - I hope this will be in core in D8 :)
    – donquixote
    Oct 7, 2011 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.
    – apaderno
    Nov 5, 2011 at 21:53
  • Just saying, people are asking for use cases. drupal.org/node/1302126#comment-5911632
    – donquixote
    May 17, 2012 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 and acknowledge you have read our privacy policy.

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