Changing the Order of the jQuery Event Queue
Wednesday, 3rd July 2013, 20:27
So jQuery is great and everything, but sometimes you have a bunch of click events (or similar) bound to a DOM element and you want to change somehow the order they happen in. But how?
This is something I found myself needing to do last week, because I had a page with a large number of custom jQuery plugins bound to it, and I wanted to prevent all further jQuery click events on a button if certain conditions were met. Simple you might ask, event handlers are triggered in the order they were bound, so just bind it first and then call stopImmediatePropogation() inside the handler.
But this isn't always possible, binding the event first, especially if you are in an environment not of your own making, which includes javascript files you have little control over before the one you add has a chance to have it's fun.
After an awful lot of fruitless googling, I did find the odd hint on how to do it, so armed with the Chrome debugger (you know the one, that mad thing that gets all your breakpoints confused when you refresh a page and triggers them in the wrong places until it's loaded) I started poking around and spamming objects to the console.
Eventually, I managed to work it out, and it's actually really simple to move an event to the front of the queue. And by understanding how the process works you should be able to re-order the queue in other ways if you feel so inclined.
Exposing the jQuery Event Queue
The first thing we need to do is get hold of the events object for a given DOM element. The problem is it's not a public piece of data as far as jQuery is concerned, so we have to use the undocumented _data function to get at it. First a tiny bit of HTML:
<a id="mybutton">Click Me!</a>
And now a tiny bit of Javascript:
var eventList = $._data($("#mybutton")[0], "events");
Before you start asking, the array index [0] reference is needed because the data is referenced from the DOM object itself and not a jQuery object. You could rewrite the above as follows and it would work just the same (and maybe a bit faster):
var eventList = $._data(document.getElementById("mybutton"), "events");
This returns a simple object linked to our anchor DOM object which has no jQuery event handlers bound to it, so is empty! Note, I said empty as in { }, not null. However if it did have a few things bound then it would look something like this:
Object { keydown: Array[2], blur: Array[2], remove: Array[1], keypress: Array[1] }
Believe it or not, we are almost nearly there, each of those arrays contains a list of event handler objects. Now before you go just adding your own functions to the list, it's worth bearing in mind these are not arrays of functions, they are arrays of objects, each of which contains various jQuery specific properties so you really don't want to go adding them yourself. Especially if you want to maintain a level of compatibility with future jQuery versions.
If you expected a full tutorial on re-ordering the event queue I'm sorry to disappoint, I've got you this far and I'm only going to take you a tiny bit further because I can't think of anything more useful than the final bit I'm going to show you, so I'll just show you that and if it's not enough for your needs then you can work the rest out yourself.
Ensuring Your Event is First in the Queue
Now for the easy bit, say we have gotten the events object into our eventList variable as described above? How do we make sure the very next click event (as an example) we bind is always executed first?
Easy! You just remove it from the end of the array for that event before any more are bound, and put it at the beginning. Like this:
$("#mybutton").click(function(event) {
if ($(this).hasClass("disabled")) {
event.stopImmediatePropagation();
return false;
}
});
var eventList = $._data($("#mybutton")[0], "events");
eventList.click.unshift(eventList.click.pop());
Easy huh?
We bind our new click event first, and that gets added as the last entry to the click property of the events object. The click property is an array, so we just pop it off the end and unshift it onto the front.
Now we can disable our button by just adding the disabled class, which gives us a two for one deal since we need to change the style so it shows users it's disabled anyway.
There are lots of other uses and needs to insert something into the beginning of an event queue in jQuery, but if you've read this far then you already have that need so I won't bore you with them. :)
posted by azghanvi on Friday, 10th November 2017, 17:31
Perfect! exactly what i was looking for.