adiktofsugar
adiktofsugar

Reputation: 1509

How do you change the event firing order of the same event?

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

Upvotes: 6

Views: 6488

Answers (2)

loganfsmyth
loganfsmyth

Reputation: 161457

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?

Upvotes: 4

nnnnnn
nnnnnn

Reputation: 150030

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.

Upvotes: 0

Related Questions