I'd like to start by saying I'm new to jQuery and I suspect I'm just doing something stupid, so hopefully this will be very simple for someone.
I'm trying to add a sliding mobile sub-menu to my website. I want an accordian effect whereby if I click one parent link, it's child sub-menu opens and all other sub-menus close. The problem is timing - the child sub-menu opens and then is closed again by the resetting of all sub-menus. I presume the answer is to use deferreds but everything I've tried has failed. This is the (currently not working) code:
function ResetMenu(){
jQuery(".mobile-menu").find(".sub-menu").slideUp(100);
jQuery(".mobile-menu").find(".menu-item-has-child").removeClass("open");
};
function OpenSubmenu(){
jQuery(this).next("ul").slideDown(100);
jQuery(this).parent().addClass("open");
};
jQuery("li.menu-item-has-children > a").click(function(){
if(jQuery(this).parent().hasClass("open")){
jQuery(".mobile-menu").find(".sub-menu").slideUp(100);
jQuery(this).parent().removeClass("open");
} else {
jQuery.when(ResetMenu()).done(OpenSubmenu());
}
return false;
});
Any help would be greatly appreciated. Thank you!
Ronel
This is a common mistake in how to use jQuery.when()
.
jQuery.when()
requires promises as arguments. It does not have magical powers to know when functions you pass it are somehow done. Those functions MUST return promises that are resolved or rejected when the underlying code is done and you can then pass those promises to jQuery.when()
.
Your ResetMenu()
function doesn't return anything so therefore, your jQuery.when()
doesn't wait for anything. It executes the .then()
handler immediately (which looks like that is not what you want).
So, in this line:
jQuery.when(ResetMenu()).done(OpenSubmenu());
ResetMenu()
MUST return a promise for jQuery.when()
to know when it is done.
You could fix ResetMenu()
to work that way by doing this:
function ResetMenu(){
return jQuery(".mobile-menu").find(".sub-menu").slideUp(100).promise().then(function() {
// remove this class when the animation has completed
jQuery(".mobile-menu").find(".menu-item-has-child").removeClass("open");
});
};
And, then further, you need to change how you pass a function to .done()
to this which both makes it just a function reference that can be executed LATER and binds a appropriate this
value to it:
jQuery.when(ResetMenu()).done(OpenSubmenu.bind(this));
Note, the .bind(this)
assumes that this
is the appropriate value. You can pass whatever value is the correct value there and that will become the this
value inside of OpenSubmenu()
when it is executed.
When you pass a non promise object to $.when()
the callbacks passed to done()
is invoked immediately, in your case when ResetMenu
is not returning anything the OpenSubmenu
is called immediately, there is another problem also - you should not invoke OpenSubmenu
directly(by adding ()
), you need to pass a function reference to done()
function ResetMenu() {
jQuery(".mobile-menu").find(".menu-item-has-child").removeClass("open");
return jQuery(".mobile-menu").find(".sub-menu").slideUp(100).promise();
};
function OpenSubmenu() {
jQuery(this).next("ul").slideDown(100);
jQuery(this).parent().addClass("open");
};
jQuery("li.menu-item-has-children > a").click(function () {
if (jQuery(this).parent().hasClass("open")) {
jQuery(".mobile-menu").find(".sub-menu").slideUp(100);
jQuery(this).parent().removeClass("open");
} else {
jQuery.when(ResetMenu()).done(OpenSubmenu);
}
return false;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With