Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
There should be more flexible ways to sort Drupal.behaviors.
Module weight is not flexible enough.
http://drupal.stackexchange.com/questions/1673/how-can-i-control-the-ord...
The Behavior weights module shows how it can be done.
This module has to work around the limitations of contrib modules, a core solution can be more powerful / elegant.
Why not module weight or drupal_add_js() weight?
Please read comment #1 by Damien Tournoud. Then read it again.
Comments
Comment #1
Damien Tournoud CreditAttribution: Damien Tournoud commentedI commented on the StackExchange question. Javascript gives no guarantee of the order of keys when traversing an object, so the current order in which the behaviors are applied is actually completely undefined.
Comment #2
Damien Tournoud CreditAttribution: Damien Tournoud commentedComment #3
donquixote CreditAttribution: donquixote commented@Damien,
I was going to add that comment myself, bug my Karma on drupal.stackexchange does not allow it yet..
http://stackoverflow.com/questions/648139/is-the-order-of-fields-in-a-ja...
http://stackoverflow.com/questions/280713/elements-order-for-in-loop-in-...
We can read that most (all relevant?) browsers behave "as we would expect".
This being said, this is even one more argument that behavior order should be controllable with a weight setting.
The attachBehaviors should look at the Drupal.behaviors object and any weight information it can find, then transform the thing into an array (or an array of objects, so the real behaviors are found one level further down the hierarchy).
Then we need some mechanics that allow other modules to manipulate the behaviors, after they have been "transformed" (so we don't have to run this every time that attachBehaviors() is called).
Comment #4
nod_That's not a good way to fix the problem. A proper solution would be: #1542344: Use AMD for JS architecture.
Comment #5
Damien Tournoud CreditAttribution: Damien Tournoud commentedHm. Not sure if this is the same problem. Dependencies between Javascript files and the other some event is been executed are two completely different things.
Comment #6
nod_Dependencies are loaded before the rest is executed, so behaviors/events are bound in order. That should do it to fix the order issue.
If not there might be something wrong with the code itself that needs to be rethought.
Comment #7
donquixote CreditAttribution: donquixote commentedCould you explain how AMD changes the order of behaviors?
The issue you linked does not seem to have a sufficient explanation (or it is hidden somewhere).
----------
"Dependencies", the way I understand it, means that one component that knows two behaviors, can tell us which of those should be executed before the other. Those components would have to take care to not declare any cyclic dependencies etc.
"Weights", on the other hand, can control the order of behaviors that are unknown to each other. That means, a component does not need to know both behaviors, to give one of them a weight of -1.
Both of these concepts have their legitimate use cases, although in practice it is often sufficient to work with weights only.
Using a combination of them could result in priority conflicts, but this is not any worse than the cyclic dependencies issue mentioned above.
----
ALso, as Damien Tournoud points out in #5, this is about order of behaviors, not the order of script files.
One script file A can register any number of behaviors, with different weights. With behavior_weights module, there could even be another script file B that would change the order (weight) of behaviors registered in script A. The load / execution order of A and B would be completely irrelevant.
Comment #8
nod_Ok what I was writing wasn't going to help, so.
Do you have a concrete use-case where weight would be needed? It would be much easier to explain what's going on.
Comment #9
donquixote CreditAttribution: donquixote commentedUse cases can be so distracting.
(I had one, but I don't remember it atm.)
Comment #10
nod_Let me know when you have something. I need to know the kind of problems to know what to account for. not to mention that's an issue to move #1446166: Use JS events instead of Drupal.behaviors forward. Thanks.
Comment #11
nod_Crossposting from #1446166: Use JS events instead of Drupal.behaviors, postponed until at least one use-case is presented.
Comment #12
donquixote CreditAttribution: donquixote commentedOk, I grepped my codebase and found two use cases.
In general, the idea is that I want to do some client-side manipulations on the html, before a mechanic (slider etc) kicks in.
E.g., set an explicit width on an element based on a dynamic calculation, before jquery.cycle does it (with the wrong values).
This was one case.
In the other case I found, on hindsight, the weight was not necessary at all :) No matter which weight I put there, the click-popup-fade mechanic did still work. Probably I just put it there during a troubleshooting, and forgot to remove.
And then there is this drupal.stackexchange discussion,
http://drupal.stackexchange.com/questions/1673/how-can-i-control-the-ord...
Unfortunately, the guy does not tell us about the use case (and I have insufficient karma to comment :) )
Comment #13
nod_In this jQuery cycle issue, can you tell me how changing the #weight attribute in the drupal_add_js wouldn't work?
As your second example showed, I suspect the need for behavior weights is to go around a weak architecture. I'm having a hard time seeing how proper code would need this (which is why I'm asking for use-cases :). And yes, the stackexchange isn't very helpful for us right now.
Comment #14
donquixote CreditAttribution: donquixote commentedThe #weight controls the order of javascript files, and thus, in which order the behaviors are added into the array.
However, As explained in the stackexchange discussion and supported by referenced articles, the order in which properties are added to an object in javascript does not guarantee the order in which they are looped through.
See
Is the order of fields in a javascript object predicatble when looping through them? and
Elements order - for (… in …) loop in javascript.
and also Damien's comment in #1.
I thought we are past this point :)
Besides, having the behavior weight as a separate mechanic, does allow us to add different behaviors with different weights, all from the same js file.
Comment #15
nod_To be honest, I'm thinking about this in the context of JS events which are in a proper FIFO queue.
If it was up to me, you wouldn't have several behaviors in the same file, what you're presenting as a feature feels more like a drawback to me. That's just my opinion, it might turn out you're right and I'm not, we'll see :)
Comment #16
donquixote CreditAttribution: donquixote commentedBtw, about use cases: I had a comment in the queue of behavior_weights:
#1700360: proper implementation
Comment #17
nod_grrr crap network ate my comment.
the short version, the issues doesn't show weights are needed. It shows something like "run this behavior after/before this one" is needed. It's in #1446166-27: Use JS events instead of Drupal.behaviors.
I'm tempted to put this one as won't fix. Or more like will fix it in a better way.
Comment #18
donquixote CreditAttribution: donquixote commentedOk, so in this case it would better be solved with dependencies, aka "run A before B".
Following #1446166-22: Use JS events instead of Drupal.behaviors and #1446166-27: Use JS events instead of Drupal.behaviors:
We move to events for the internal implementation, and this will change the signature of attach() methods to expect an event argument.
However, the registration of behaviors would still be quite similar: You create an object Drupal.behaviors.my_behavior with an attach() method, and drupal.js will register it automatically. (If I understand correctly).
Instead of the module js explicitly saying ".bindBefore()", I think it would be preferable if you can simply give the behavior object a dependencies attribute.
E.g.
Drupal.behaviors.my_behavior.run_before.another_behavior = true;
As mentioned in #7, we can have both, or only one of: behavior.weight, and behavior.run_before / .run_after.
This said: I wonder how event handler dependencies can be dealt with outside Drupal behaviors attach() stuff. How does it work with jQuery events, for instance?
And how can .bindBefore() recognize string keys, if events are registered with numeric keys? (remember, string key attributes have undefined order)
Comment #19
nod_Let's take that in the other thread. can you repost there? there are some valid questions and things that could use clearing up.
Comment #20
nod_Still no valid use case.
Comment #21
cweagansI just ran into a place where I was writing a behavior that needed to be executed after a behavior in another file, and to my astonishment, there was no way to order behaviors in core and I had to download a contrib module to make it happen.
IMO, this should be in core. We have use cases for wanting to run hook implementations in a certain order, and I don't see any reason we can't apply that same logic to Javascript behaviors.
The other issue hasn't had much movement in a couple of months, so reopening this so that we can maybe get in a quick 'n' dirty method of reordering behaviors before it's too late.
Comment #22
nod_I strongly disagree. The use case is having a way to run behaviors after/before another one. Weights are a poor way of solving this problem.
We're trying to get rid of the weights in library declaration because they are more harmful than helpful #1945262: Replace custom weights with dependencies in library declarations; introduce "before" and "after" for conditional ordering, it's not to add them to our JS now. It'll mess up contrib and we won't be able to go back from it it's not the time for quick & dirty. Also I'm planning on making the whole AMD thing in contrib, having weights would make that way harder than it should be.
Have a look at #1446166: Use JS events instead of Drupal.behaviors for a better, event based, solution. We should keep in mind the objective, this is just one (poor) solution to the problem.
Comment #23
donquixote CreditAttribution: donquixote commentedThere are actually two use cases, as explained in #7. One is best solved with weights, the other is best solved by saying "A must run before B".
Comment #24
nod_Is that what you're refering to?
In which cases would you want to order behaviors that are unknown to each other? It seems to me that if they don't know about each other, they have no business trying to execute before or after another.
Comment #25
donquixote CreditAttribution: donquixote commentedIt is more like "I want to run later than most of the other behaviors" or "I want to run before most of the other behaviors". If you are very sure about this, you choose weight = 999 or -999. If you want to give other behaviors a chance, you choose 10 or -10 instead.
This is the same with modules and hooks.
Finding a specific use case is difficult because most of the time I spend on PHP, not on javascript. Or if I develop some javascript, I rarely get in touch with behaviors.
Comment #26
nod_That's actually part of my point. I don't want to encourage a bad practice. Reasoning this way (and relying on weights) will conflict with the nature of JS and will end up limiting what we can do. JS is inherently async and weights don't encourage that what JS is going towards are events, callbacks, promise and all.
Let's say we have an ordered list of behaviors. If we run them in order there is no guarantee the previous behavior will be completely finished before launching the next one (there are behaviors with ajax calls in them, see edit module) or if webworkers are involved somehow. That'll confuse users and they'll complain to us that weights don't work.
Comment #27
donquixote CreditAttribution: donquixote commentedThis would be the same with other ways of prioritization ("A after B" style). The synchronous part is ordered, the asynchronous part is not.
To clarify: I do not have a strong use case atm. The one mentioned by cweagans seems to be more of the "A after B" style, where both A and B are known. I am defending my argument, but do not necessarily insist on a specific outcome.
Comment #27.0
donquixote CreditAttribution: donquixote commentedpointing to comment #1, which points out that drupal_add_js() weight or module weight is not a reliable solution.
Comment #28
nod_Stopgap solution at #2367655: Control the list of behaviors run by Drupal.attachBehaviors. The AMD thing is way, way in the future.
It will give a way to order behaviors and/or filter them properly.
Comment #29
nod_Actual issue for the duplicate thing: #2474019: Implement before and after behavior ordering
Comment #30
donquixote CreditAttribution: donquixote as a volunteer commentedJust for the record:
https://www.drupal.org/project/behavior_weights now reports 890 installs.
And I just ran into a use case myself, where I want to run a custom behavior after a contrib behavior (atom_reference.js, in scald project), to alter the html generated there (for the
<div class="atom_reference_operations">
).This would actually be better (or semantically cleaner) solved by the dependency model as suggested by nod_, instead of numeric weights. But the numeric weights will do the trick.
(We can discuss whether atom_reference.js does a good thing here, but let's just assume it is unchangeable.)
Comment #31
TenaMurphy CreditAttribution: TenaMurphy as a volunteer commentedIn Drupal 7, I simply wanted the js in my custom module to run AFTER the js in a contrib module. This solved the problem for me:
$path_to_js = drupal_get_path('module', 'MODULE_NAME_HERE') . '/SCRIPT_NAME_HERE.js';
drupal_add_js($path_to_js, array('weight' => 1));