11487 sujets

JavaScript, DOM et API Web HTML5

Bonjour,

Je suis en train de créer un dropdown menu simple en jQuery, avec l'utilisation de max-height pour afficher ou masquer le sous menu. Ca fonctionne sans problème, seulement je m'aperçois que lorsque je re-clique pour fermer le menu, il y a une latence avant que le script s'exécute. C'est systématique et uniquement sur la fermeture. Quelqu'un a déjà eu affaire à ce problème ?

Voici le code :
http://codepen.io/anon/pen/rOBdra

Merci pour votre aide Smiley smile
Je viens de comprendre quelque chose ; le fait d'attribuer un max-height important, fait que l'animation est calculé pour cette taille, même si le block ne dépasse pas les 100px.

Utiliser une transition sur le max-height n'est donc pas une bonne façon de procéder.
Administrateur
neovea a écrit :
C'est systématique et uniquement sur la fermeture. Quelqu'un a déjà eu affaire à ce problème ?

Bonsoir,

Oui c'est parce que sa valeur de départ de calcul pour sa transition retour a été fixée à 1000px
Administrateur
Raphael a écrit :

Bonsoir,

Oui c'est parce que sa valeur de départ de calcul pour sa transition retour a été fixée à 1000px


neovea a écrit :
Utiliser une transition sur le max-height n'est donc pas une bonne façon de procéder.


Il n'y a malheureusement pas beaucoup d'autres manières de faire, étant donné qu'on ne peut pas transiter sur la valeur "auto"
Bonjour,

il existe un petit hack JS qui permet d'animer sur des propriétés "auto" :
jQuery.fn.animateAuto = function(prop, speed, callback){
    var elem, height, width;
    return this.each(function(i, el){
        el = jQuery(el), elem = el.clone().css({"height":"auto","width":"auto"}).appendTo("body");
        height = elem.css("height"),
        width = elem.css("width"),
        elem.remove();
        
        if(prop === "height")
            el.animate({"height":height}, speed, callback);
        else if(prop === "width")
            el.animate({"width":width}, speed, callback);  
        else if(prop === "both")
            el.animate({"width":width,"height":height}, speed, callback);
    });  
}

jQuery(document).ready(function($){
  $('.side-nav .has-dropdown > a').on('click', function(e){
        e.preventDefault();
        var $ulToOpen = $(this).next('ul');
    
        if ( $ulToOpen.hasClass('open') ) {
          $ulToOpen.removeClass('open')
          $ulToOpen.animate({'height':'0px'}, 1000)
        } else {
          $ulToOpen.addClass('open');
          $ulToOpen.animateAuto('height', 1000)
        }
    });
});


EDIT : le principe est simple, on clone le bloc que l'on veut animer sur la page en visible, on récupère sa taille, puis on le supprime (donc en fait on a normalement pas le temps de le voir), et enfin on anime le vrai bloc sur la taille récupérée.
Modifié par SolidSnake (02 Sep 2015 - 14:35)
Ah j'ai également oublié, pour fermer les blocs précédemment ouverts, il faudra ajouter la ligne :
$('ul.dropdown.open').not($ulToOpen).removeClass('open').animate({'height':'0px'}, 1000)

... avant le if()

EDIT : un petit LIEN
Modifié par SolidSnake (02 Sep 2015 - 14:43)
Il y a également la possibilité de spécifier le max-height pour chaque sous-menu et de le refermer avec .animate(), cela fera disparaitre la latence de fermeture. Par contre pour l'ouverture il faut pas mettre 1000px, mais plutôt la valeur la plus élevée des sous-menu.
SolidSnake a écrit :
Bonjour,

il existe un petit hack JS qui permet d'animer sur des propriétés "auto" :
jQuery.fn.animateAuto = function(prop, speed, callback){
    var elem, height, width;
    return this.each(function(i, el){
        el = jQuery(el), elem = el.clone().css({"height":"auto","width":"auto"}).appendTo("body");
        height = elem.css("height"),
        width = elem.css("width"),
        elem.remove();
        
        if(prop === "height")
            el.animate({"height":height}, speed, callback);
        else if(prop === "width")
            el.animate({"width":width}, speed, callback);  
        else if(prop === "both")
            el.animate({"width":width,"height":height}, speed, callback);
    });  
}

jQuery(document).ready(function($){
  $('.side-nav .has-dropdown > a').on('click', function(e){
        e.preventDefault();
        var $ulToOpen = $(this).next('ul');
    
        if ( $ulToOpen.hasClass('open') ) {
          $ulToOpen.removeClass('open')
          $ulToOpen.animate({'height':'0px'}, 1000)
        } else {
          $ulToOpen.addClass('open');
          $ulToOpen.animateAuto('height', 1000)
        }
    });
});


EDIT : le principe est simple, on clone le bloc que l'on veut animer sur la page en visible, on récupère sa taille, puis on le supprime (donc en fait on a normalement pas le temps de le voir), et enfin on anime le vrai bloc sur la taille récupérée.


Merci BigBoss et Oken pour vos réponses.
Autre possibilité : jQuery UI Slide effect : http://api.jqueryui.com/slide-effect/

Je l'avais testé pour les besoins d'un menu horizontal multi niveau et ça fait un job d'enfer en très peu de lignes de codes. J'ai tendance à préférer les solutions simples et éprouvées.
Cependant, la nature de max height pourra très probablement m'amener à utiliser un hack Smiley cligne

Merci encore Smiley smile