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
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.behaviors
is 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.– apaderno ♦Mar 31, 2011 at 15:43
-
1You can always just check in the system table - try
select name, weight from system where type = 'module' order by weight ASC, name
– cam8001Mar 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?– cam8001Mar 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
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. Oct 7, 2011 at 3:59
-
-
-
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 May 17, 2012 at 14:41
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.