Currently Drupal.CTools.Modal.modal_display does two things wrong:
1) Never detaches js behaviors from previous modal content.
2) performs attachbehaviors on global scope (window.document) by not explicitly passing any context.

During modal display ctools is responsible for changing content only inside the modal, so lets localize attachbehaviors to modal content only.

Also before new behaviors are attached, we should be nice to possible previously attached behaviors by detaching them before content is replaced.

Comments

litwol’s picture

Status: Active » Needs review
StatusFileSize
new494 bytes

Cheers.

tim.plunkett’s picture

Status: Needs review » Needs work
+++ b/js/modal.jsundefined
@@ -281,9 +281,10 @@
+    Drupal.detachBehaviors($('#modal-content'));

Maybe assign a variable for $('#modal-content')?

litwol’s picture

Status: Needs work » Needs review
StatusFileSize
new570 bytes

Check it :)

merlinofchaos’s picture

Status: Needs review » Closed (won't fix)
igor.ro’s picture

@merlinofchaos, I have read your comments about behaviors problem.
Your ideas are good and I can agree that :not(processed-X) is better then context case.
I have found problem that when we do such way

 Drupal.attachBehaviors($('#modal-content'));

and we have behaviors like this

  Drupal.behaviors.test = function (context) {
      $('#modal-content', context).click(function() {
         .....
      });
  }

This code would not find #modal-content because it search only in context's child. This is the big problem with behaviors.
In other hand we have a lot of contrib and custom modules follow context agreement.
and when I start using ctools modal on my current project I got a lot of bugs with processing the same data twice.
The problem was that ctools modal do not follow drupal behaviors agreement. Even context way is bad.

So currently I have forked ctools module on my project to fix this. I think some of the other 317538 projects used ctools could have the same problem (btw it is not easy to find such bugs out).

This is my minds about.
Thanks

merlinofchaos’s picture

Even if I sent behaviors the way you suggest, the code you want wouldn't work, because of the way jquery works.

You would be better off avoiding the use of 'context' entirely (PLEASE see the link provided to Matt Butcher's excellent post on the topic) than forking CTools. In fact, forking a module over a philosophical question like this is a really, really, really bad idea and is goint to eventually burn you no matter what, especially when you can fix your code in about 3 minutes and it will work.

merlinofchaos’s picture

In fact, if you're willing to fork CTools over this, I think you should probably just write your own modal instead. It doesn't make sense that you'd do this, and I actually find it upsetting. If you do choose to stick with your fork, please do not ask for support.

igor.ro’s picture

Yes, code I have posted in comment #5 would not work.
It was the example of problem when we use context.
That was example to show that you are right about "using context is a bad idea".

Absolutly agree with you that module fork because of philosophical question is a stupid idea.
I'm speaking not about philisophy, I'm speaking about bugs.
A lot of drupal module use behaviors in case that whole document will pass only once.

Current code makes ajax forms that send's twice (or more) if some one open modal window and close it.
Some DOM elements have several same functions on events. And etc...

When we are speaking about your examples, like rows in tables - this is responsibility of behavior.
Behavior should ignore context in it's code if it is necessarily.

If Drupal follow your idea we will solve all problems, discribed in answer to Matt Butcher's excellent post on the topic.
But now this code makes bugs.

This code will works fine.

Drupal.attachBehaviors($('#modalContent'));

http://drupal.org/files/ctools-js-behaviours-context-1516062-2.patch

Thank you.

tim.plunkett’s picture

When we are speaking about your examples, like rows in tables - this is responsibility of behavior.
Behavior should ignore context in it's code if it is necessarily.

Actually, this seems like a fair point to me. If we accept that using context is broken in a use-case, we just should use it in the selector.

merlinofchaos’s picture

What bugs? Behaviors *must* assume that behaviors on the entire document might run more than once. The modal is *not* the only thing that might run behaviors; there could easily be some other AJAX call that happens on the page that might cause behaviors to run; even if I did fix this 'bug', other things could cause it.

The contract of behaviors requires you to use something like .once() to ensure your behaviors do not run multiple times on your own code. That is not the fault of CTools. This is a bug in your code and trying ot lay the blame on CTools is even worse .

igor.ro’s picture

>> The contract of behaviors requires you to use something like .once() to ensure your behaviors do not run multiple times on your own code.
I did not find any information about this in documentation.
http://drupal.org/node/304258#drupal-behaviors
But I have found

With the above set-up, though, all you need to do is call Drupal.behaviors.myModuleBehavior(newcontext), where newcontext would be the new, AJAX-delivered content, thus ensuring that the behaviors don't get attached to the whole document all over again.

litwol’s picture

@ igor.ro, instead of arguing back and forth, i recommend you take a look at code in core as well as some popular modules and see how they do it. you will discover that all of them use .once() method or equivalent selector behavior in $('somthing:not(.class-name)').addClass('class-name') ... If you dont understand what that does, that is a different issue and we can have discussion about that. but i assure you it is the right way to go about it. so please stop arguing about what is in documentation and what is not.

Good luck.

igor.ro’s picture

looks like your point of view is right.
Thanks for discussion.

http://drupal.org/node/114774#javascript-behaviors

Drupal.behaviors.myBehavior = function (context) {
$('.my-class:not(.myBehavior-processed)', context).addClass('myBehavior-processed').each(function () {
// Process...
});
};