2

In jQuery, I'm trying to add a "submit" binding to a form which already has one "submit" binding, but I need the new (second) binding (callback) to fire before the pre-existing one.

Is there a way for me to insert a binding so that it fires ahead of a binding that is already been attached?

JellicleCat
  • 28,480
  • 24
  • 109
  • 162

4 Answers4

2

You could use $._data( $("#myform")[0], "events" ).submit

See a sample jsfiddle for click event:

http://jsfiddle.net/X9hat/

A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • +1. Great fiddle and great answer. My intention is to prevent the other bindings from firing, but when I `return false` from the newly inserted binding, I find it doesn't prevent the other actions. (http://jsfiddle.net/X9hat/1/) Can you tell me what I have to do to prevent the other bindings? (I don't wish to entirely lose them. The new first binding should perform a validation check, then determine whether the others must fire.) – JellicleCat Apr 15 '13 at 14:54
  • I'll look if i can find a solution soon – A. Wolff Apr 15 '13 at 15:36
  • Found it. Thanks. I get the callback and unbind the original binding with: `$._data( $("#pq-new-quoter")[0],"events" ).submit.pop().handler;` – JellicleCat Apr 15 '13 at 15:52
2

Using the undocumented access to the bound functions, supposing your form has id a, you could reverse the order of handler calls like this :

[].reverse.call($._data($('#a').get(0)).events.submit);

Demonstration (Click "Run with JS", open the console, then click the "click" button)

Note that this structure is undocumented and without guarantee :

Note that this is not a supported public interface; the actual data structures may change incompatibly from version to version.

So the real solution would be to change the first binding code.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
1

Trigger the first binding in a callback to the new (second) binding: How to order events bound with jQuery

Community
  • 1
  • 1
Derek Henderson
  • 9,388
  • 4
  • 42
  • 71
  • Ok, so can I unbind the first callback and still have a reference to that function so that I may bind it to the new callback function? (The original callback function is encapsulated, so I can't call it by name.) – JellicleCat Apr 15 '13 at 14:44
  • @JellicleCat, why unbind? Simply don't bind the first callback until you bind it in the callback to the second binding. – Derek Henderson Apr 15 '13 at 14:48
  • I'm using a third-party script from a CDN, so I can't change the original binding. – JellicleCat Apr 15 '13 at 15:18
  • @JellicleCat So then, yes, first unbind the original binding, then rebind it in the callback to the new binding. – Derek Henderson Apr 15 '13 at 15:50
0

i know this is a little late, but jQuery exposed this in v1.7 and higher. I have a meeting and will turn this into a working example when i get back ... if I get back.... wish me luck.

#fiddle this runs off the click event, but you can switch it to submit with thought

html

reposting the html from jsfiddle

<form>
    <p>before click</p>
    <button type="button">start</button>
</form>

javascript

reposting the javascript from jsfiddle

var $elem = $("button"), //jqElementObject
    elem=$elem[0], //html element object
    eventNameKey="click", //name of the event we want to alter
    eventHandlerKey="handler"; //jQuery private object reference to get the actual function ref

 $elem.click(function(ev){
     //this happens somewhere else, pretend we can't control it per your comments
    console.log("1");
    ev.preventDefault();
  $("p").text($("p").text()+"<div>first binding</div>");
});
//this is ours, we can control this
function submitControler(ev){
        console.log("2");
  ev.preventDefault();
  $("p").text($("p").text()+"<div>second binding</div>");
    //uncomment below to stop propogation
    //return false;
}
$elem.click(submitControler); //actually register our event
$elem.click(function(ev){ //third event for some fun
ev.preventDefault();
  $("p").text($("p").text()+"<div>third binding</div>");
});

 var officialEvents = $._data(elem,"events"), //jQuery official Private events
    ourSubmitEvents = $.Callbacks("unique stopOnFalse"); //default Callbacks values promises to behave like a standard events list, however, you want your event to fire first, then others to proceed if it returns true.  also, no duplicates just because.

//I'm running out of time, so i went with a for loop. I wanted a way to find our event where the registration order was not guaranteed, feel free to go for something better
for(var event = officialEvents[eventNameKey].length-1; event >-1; event--){
    if(officialEvents[eventNameKey][event][eventHandlerKey] ===submitControler){
      ourSubmitEvents.add(officialEvents[eventNameKey][event][eventHandlerKey]);
      console.log("added primary controler");
      break;
    }
}
//add in everyone else to our list, again, i'm out of time and a for loop was the first thought i had.  feel free to improve
for(var event = 0; event < officialEvents[eventNameKey].length; event++){
    if(officialEvents[eventNameKey][event][eventHandlerKey] !==submitControler){
      ourSubmitEvents.add(officialEvents[eventNameKey][event][eventHandlerKey]);
        console.log("added secondary controler");
    }
}
//this is our event scheduler, it will be first to execute, and calls our manually re-ordered 'ourSubmitEvents' object. ('ourSubmitEvents' becomes 'ev.data')
$elem.on({"click":function(ev){
    ev.preventDefault();
  ev.data.fire(ev);
}},null,ourSubmitEvents);
//remove the other events, except the one we just registered.
$._data(elem,"events")[eventNameKey].splice(0,officialEvents[eventNameKey].length-1);
DefyGravity
  • 5,681
  • 5
  • 32
  • 47
  • also, feel free to use a shorter answer @dystroy below. anything that hits $._data() is the only way to go, I used the $.CallBacks() for organizing the events post manipulation and for finer grain control – DefyGravity Apr 15 '13 at 20:47