Reputation: 52047
I have a class of div MyClass I want to hook up to two handlers. This class is generated dynamically and this is what I'm doing:
$('#MainDiv').on({
click: function () { Handler1($(this)); }
}, '.MyClass');
$('#SpecialChildDiv').on({
click: function () { Handler2($(this)); }
}, '.MyClass');
How do I control which handler is called first? Handler2 does some additional work but it's not called on every MyClass because it's not in the SpecialChildDiv. I want Handler 2 to be called AFTER Handler1.
Thanks.
Upvotes: 0
Views: 62
Reputation:
Because the SpecialChildDiv
is presumably a descendant of MainDiv
, it will be called first. This is because the event bubbles up from the target, and invokes handlers found along the way up in that order.
One possibility would be to make the call to Handler2
in the SpecialChildDiv
handler asynchronous by using a setTimeout
.
$('#MainDiv').on({
click: function () {
Handler1($(this));
}
}, '.MyClass');
$('#SpecialChildDiv').on({
click: function () {
setTimeout($.proxy(function() {
Handler2($(this));
}, this), 0);
}
}, '.MyClass');
So the handler itself will be invoked right away, but your Handler2
function won't run until the after the Handler1
has been called.
I've also used $.proxy
to retain the correct this
value in the function you pass to setTimeout
.
A little off topic, but you can make your code cleaner if you change Handler1
and Handler2
so that this
is a reference to the element, instead of passing it as an argument...
$('#MainDiv').on({
click: Handler1
}, '.MyClass');
$('#SpecialChildDiv').on({
click: function () {
setTimeout($.proxy(Handler2, this), 0);
}
}, '.MyClass');
You'll just need to do $(this)
inside your functions to wrap the element in a jQuery object. Though this may not be useful if there are other arguments to pass.
Based on your comment below, it seems that this technique will need to be applied to various descendant elements.
To DRY up the code, you can create a factory for your click handlers...
function async_handler_factory(the_handler) {
return function() {
setTimeout($.proxy(function() {
the_handler($(this));
}, this), 0);
};
}
...then use it like this...
$('#MainDiv').on({
click: Handler1
}, '.MyClass');
$('#SpecialChildDiv').on({
click: async_handler_factory(Handler2)
}, '.MyClass');
$('#AnotherChildDiv').on({
click: async_handler_factory(Handler3)
}, '.MyClass');
So it's the same principle. Just that you pass your HandlerN
function to async_handler_factory
, and it will create and return the click
handler function, which does the same as my original code by invoking the HandlerN
in a setTimeout
.
Just to avoid confusion about $.proxy
, here's an updated version that eliminates it.
function async_handler_factory(the_handler) {
return function() {
var self = this;
setTimeout(function() {
the_handler($(self));
}, 0);
};
}
This shows that it's actually the setTimeout
that is making your solution work.
EDIT: Had a different name for async_handler_factory
in different places. Fixed.
Upvotes: 1
Reputation: 92983
So don't call the second one on click -- call it after the first handler is done:
$('#MainDiv').on({
click: function (e) {
Handler1($(this));
if ($(e.target).attr('id')==="SpecialChildDiv") {
specialChildClick();
}
}
}, '.MyClass');
specialChildClick = function () {
Handler2($('#SpecialChildDiv'));
}
Upvotes: 0