web-dev-qa-db-fra.com

jQuery .when (). done () ne fonctionne pas

J'aimerais commencer en disant que je suis nouveau sur jQuery et je soupçonne que je fais juste quelque chose de stupide, alors j'espère que ce sera très simple pour quelqu'un.

J'essaie d'ajouter un sous-menu mobile coulissant à mon site Web. Je veux un effet accordéon par lequel si je clique sur un lien parent, son sous-menu enfant s'ouvre et tous les autres sous-menus se ferment. Le problème est le timing - le sous-menu enfant s'ouvre puis se referme par la réinitialisation de tous les sous-menus. Je suppose que la réponse est d'utiliser des reports mais tout ce que j'ai essayé a échoué. Voici le code (actuellement non fonctionnel):

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;
});

Toute aide serait grandement appréciée. Je vous remercie!

Ronel

13
Ronelz

Il s'agit d'une erreur courante dans la façon d'utiliser jQuery.when().

jQuery.when() requiert des promesses comme arguments. Il n'a pas de pouvoirs magiques pour savoir quand les fonctions que vous lui transmettez sont en quelque sorte effectuées. Ces fonctions DOIVENT retourner des promesses qui sont résolues ou rejetées lorsque le code sous-jacent est terminé et vous pouvez ensuite transmettre ces promesses à jQuery.when().

Votre fonction ResetMenu() ne retourne rien donc votre jQuery.when() n'attend rien. Il exécute immédiatement le gestionnaire .then() (qui ne ressemble pas à ce que vous voulez).

Donc, dans cette ligne:

jQuery.when(ResetMenu()).done(OpenSubmenu());

ResetMenu() DOIT retourner une promesse pour que jQuery.when() sache quand c'est fait.

Vous pouvez corriger ResetMenu() pour fonctionner de cette façon en procédant comme suit:

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");
    });
};

Et puis, vous devez changer la façon dont vous passez une fonction à .done() à ce qui en fait à la fois juste une référence de fonction qui peut être exécutée PLUS TARD et lui lie une valeur this appropriée :

jQuery.when(ResetMenu()).done(OpenSubmenu.bind(this));

Notez que la .bind(this) suppose que this est la valeur appropriée. Vous pouvez y passer n'importe quelle valeur qui est la valeur correcte et qui deviendra la valeur this à l'intérieur de OpenSubmenu() lors de son exécution.

25
jfriend00

Lorsque vous passez un objet non promis à $.when(), les rappels passés à done() sont invoqués immédiatement, dans votre cas lorsque ResetMenu ne renvoie rien, le OpenSubmenu est appelé immédiatement, il y a aussi un autre problème - vous ne devez pas appeler OpenSubmenu directement (en ajoutant ()), vous devez passer une référence de fonction à 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;
});
3
Arun P Johny