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

Damien Tournoud’s picture

I 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.

Damien Tournoud’s picture

Title: weights for Drupal.behaviors » Weights for Drupal.behaviors
donquixote’s picture

@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).

nod_’s picture

Status: Active » Closed (won't fix)

That's not a good way to fix the problem. A proper solution would be: #1542344: Use AMD for JS architecture.

Damien Tournoud’s picture

Status: Closed (won't fix) » Active

Hm. 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.

nod_’s picture

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.

donquixote’s picture

Could 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.

nod_’s picture

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.

donquixote’s picture

Use cases can be so distracting.
(I had one, but I don't remember it atm.)

nod_’s picture

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.

nod_’s picture

Status: Active » Postponed (maintainer needs more info)

Crossposting from #1446166: Use JS events instead of Drupal.behaviors, postponed until at least one use-case is presented.

I don't think the behavior weight is an important issue. Currently the behavior weight module has 15 install and I haven't been presented with a single use-case for them yet (I'm really waiting for some!).

donquixote’s picture

Ok, 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 :) )

nod_’s picture

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.

donquixote’s picture

In this jQuery cycle issue, can you tell me how changing the #weight attribute in the drupal_add_js wouldn't work?

The #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.

nod_’s picture

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 :)

donquixote’s picture

Btw, about use cases: I had a comment in the queue of behavior_weights:
#1700360: proper implementation

nod_’s picture

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.

donquixote’s picture

Ok, 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)

nod_’s picture

Let's take that in the other thread. can you repost there? there are some valid questions and things that could use clearing up.

nod_’s picture

Status: Postponed (maintainer needs more info) » Closed (won't fix)

Still no valid use case.

cweagans’s picture

Status: Closed (won't fix) » Active

I 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.

nod_’s picture

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.

donquixote’s picture

The use case is having a way to run behaviors after/before another one. Weights are a poor way of solving this problem.

There 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".

nod_’s picture

Is that what you're refering to?

"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.

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.

donquixote’s picture

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.

It 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.

nod_’s picture

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.

donquixote’s picture

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)

This 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.

donquixote’s picture

Issue summary: View changes

pointing to comment #1, which points out that drupal_add_js() weight or module weight is not a reliable solution.

nod_’s picture

Issue summary: View changes
Status: Active » Closed (duplicate)
Related issues: +#2367655: Control the list of behaviors run by Drupal.attachBehaviors

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.

nod_’s picture

Actual issue for the duplicate thing: #2474019: Implement before and after behavior ordering

donquixote’s picture

Just 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.)

TenaMurphy’s picture

In 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));