6

Jquery bind is amazing, but I don't know in what order the binding happens. My current problem is thus:

$(document.body).delegate('form', methods.checkForm);
$('form').bind('submit', methods.submitFormIfCheckedFormIsTrue);

methods.checkForm = function (e) {
    if (!$(this).isVerified()) {
        return false;
    }
    return true;
};
methods.submitFormIfCheckedFormIsTrue = function (e) {
    e.preventDefault();
    $.ajax("/submitForm", {
        data:$(this).serialize(),
        type:"post"
    });
};

This is obviously not the actual code that I'm using, but it's pretty close. What happens is, the submitFormIfCheckedFormIsTrue function fires before, or during, the checkForm function, so the checking is pretty useless. My hack for it was to add the class "checked" to the form and the check if the form has that class in the other function...but then you click submit, it checks, then you have to click it again to submit it if everything went right...which is retarded.

Another thing that's important regarding this problem is that I'm they're in completely different parts of my application, for reasons that can't change. Also, they're being loaded asynchronously.

The main thing I want to know then...is how to change the order, or set the priority of the events somehow...

adiktofsugar
  • 1,439
  • 14
  • 17
  • Call the function you want to run second from within the first? – David Thomas Feb 15 '12 at 23:09
  • Why not just call the ajax function and check inside the same function? – loganfsmyth Feb 15 '12 at 23:09
  • It needs to be modularized. The check function is general across all forms, whereas the second function is unique to only the one particular form. I just posted that code as an example of what leads to the situation... – adiktofsugar Feb 15 '12 at 23:10

2 Answers2

4

If you are using 'delegate' the way you have it in your example, then the ajax submission is always going to run first, so the short answer to your question is "You Can't". Your delegate is attached to the 'body' element, so events attached to elements closer to the form in the DOM tree will fire first.

Events bubble from the form -> body, so there is no ordering when you are doing that.

One option would be to have your verification trigger a second event.

methods.checkForm = function (e) {
  e.preventDefault()

  if ($(this).isVerified()) {
    $(this).trigger('form-verified');
  }
};

Then instead of binding the other handler to 'submit', you would bind it to 'form-verified'.

$('form').bind('form-verified', methods.submitFormIfCheckedFormIsTrue);

This is also another way to accomplish ordering event if they are attached to the same element instead of using delegate.

Also, if you are using jQuery >= 1.7, then you should be using on instead of bind and delegate. http://api.jquery.com/on/

Update

If both are bound to the same element, then they will be triggered in the order that they were attached to the element. Assuming checkForm is bound before the other one, then the issue is that return false; does not stop other events from firing if they are attached to the same element. For that you also need e.stopImmediatePropagation().

methods.checkForm = function (e) {
  e.preventDefault()

  if (!$(this).isVerified()) {
    e.stopImmediatePropagation();
  }
};

There is also a useful answer over here if you ever have to tweak the ordering of events. jQuery event handlers always execute in order they were bound - any way around this?

Community
  • 1
  • 1
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • I like that. I'm using it, even though it doesn't really answer my question.. I also changed it so that it binds to the form. First the checkForm, then the other...hopefully..since it's asynchronous I don't really know....but it still did the same thing. Is there any way to alter the priority of events? – adiktofsugar Feb 15 '12 at 23:26
  • So you're attaching them both directly to the form again, and they are still running in the wrong order? That doesn't make sense. If the second one is bound to 'form-verified', then it won't run at all until the first one runs. Can you update your post maybe with more code? – loganfsmyth Feb 15 '12 at 23:31
  • No, using the answer you gave, it works, but I don't like having to create a custom trigger for a submit, so I'd like to know if there's a way to do what I was trying to do, which is still bind to submit, but have it execute in the proper order. – adiktofsugar Feb 15 '12 at 23:37
0

In a general sense event handlers will be called in the order that they were bound, but only if they're bound at the same level. In your case you're binding one directly to the form while the other is a delegated handler bound at the document.body level. The directly bound one will happen first and then the event bubbles up to be handled by the other.

If you bind both handlers at the same level with .delegate() then they should be called in order:

$(document.body).delegate('form', 'submit', methods.checkForm);
$(document.body).delegate('form', 'submit',
                                  methods.submitFormIfCheckedFormIsTrue); 

Then in the first (generic) handler you should call the event.stopImmediatePropagation() method to prevent other handlers being called (noting that simply returning false prevents the default and stops the event bubbling up further, but it doesn't stop other handlers at that level from running):

methods.checkForm = function (e) {
  if (!$(this).isVerified()) {
      e.stopImmediatePropagation();
      return false;
  }
  return true;
};  

(By the way, the code shown in the question left out the event (second param) from the .delegate() call - I've put it in in my code.)

Or bind both handlers directly rather than using .delegate(). And speaking of using .delegate(), if you're using the latest version of jQuery you may like to switch over to using .on(), the new do-everything event binding method.

"What happens is, the submitFormIfCheckedFormIsTrue function fires before, or during, the checkForm function"

Definitely before, not during. (In pretty much all browsers) JavaScript runs on a single thread, so you will not ever have two functions running simultaneously.

nnnnnn
  • 147,572
  • 30
  • 200
  • 241