web-dev-qa-db-fra.com

Comment puis-je changer de hauteur: 0; à hauteur: auto; en utilisant CSS?

J'essaie de faire glisser un <ul> à l'aide de transitions CSS.

Le <ul> démarre à height: 0;. En vol stationnaire, la hauteur est définie sur height:auto;. Cependant, cela le fait apparaître simplement, pas transition,

Si je le fais de height: 40px; à height: auto;, il glissera alors jusqu'à height: 0;, puis passera brusquement à la bonne hauteur.

Sinon, comment pourrais-je faire cela sans utiliser JavaScript?

#child0 {
  height: 0;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent0:hover #child0 {
  height: auto;
}
#child40 {
  height: 40px;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent40:hover #child40 {
  height: auto;
}
h1 {
  font-weight: bold;
}
The only difference between the two snippets of CSS is one has height: 0, the other height: 40.
<hr>
<div id="parent0">
  <h1>Hover me (height: 0)</h1>
  <div id="child0">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
<hr>
<div id="parent40">
  <h1>Hover me (height: 40)</h1>
  <div id="child40">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
1910
Hailwood

Utilisez max-height dans la transformation et non height. Et définissez une valeur sur max-height sur une valeur supérieure à celle de votre boîte.

Voir démo de JSFiddle fourni par Chris Jordan dans un autre réponse ici.

#menu #list {
    max-height: 0;
    transition: max-height 0.15s ease-out;
    overflow: hidden;
    background: #d5d5d5;
}

#menu:hover #list {
    max-height: 500px;
    transition: max-height 0.25s ease-in;
}
<div id="menu">
    <a>hover me</a>
    <ul id="list">
        <!-- Create a bunch, or not a bunch, of li's to see the timing. -->
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>
2518
jake

Vous devriez utiliser scaleY à la place.

HTML:

<p>Here (scaleY(1))</p>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

CSS:

ul {
    background-color: #eee;
    transform: scaleY(0);    
    transform-Origin: top;
    transition: transform 0.26s ease;
}

p:hover ~ ul {
    transform: scaleY(1);
}

J'ai créé une version préfixée par le fournisseur du code ci-dessus sur jsfiddle, http://jsfiddle.net/dotnetCarpenter/PhyQc/9/ et modifié votre jsfiddle pour qu'il utilise scaleY au lieu de height, http://jsfiddle.net/dotnetCarpenter/7cnfc/206/ .

278
dotnetCarpenter

Vous ne pouvez pas actuellement animer en hauteur lorsque l’une des hauteurs concernées est auto, vous devez définir deux hauteurs explicites.

187
robertc

La solution que j’ai toujours utilisée était de commencer par disparaître puis de réduire les valeurs font-size, padding et margin. Cela ne ressemble pas à un balayage, mais cela fonctionne sans un height ou max-height statique.

Exemple de travail:

/* final display */
#menu #list {
    margin: .5em 1em;
    padding: 1em;
}

/* hide */
#menu:not(:hover) #list {
    font-size: 0;
    margin: 0;
    opacity: 0;
    padding: 0;
    /* fade out, then shrink */
    transition: opacity .25s,
                font-size .5s .25s,
                margin .5s .25s,
                padding .5s .25s;
}

/* reveal */
#menu:hover #list {
    /* unshrink, then fade in */
    transition: font-size .25s,
                margin .25s,
                padding .25s,
                opacity .5s .25s;
}
<div id="menu">
    <b>hover me</b>
    <ul id="list">
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>

<p>Another paragraph...</p>
109
Steven Vachon

Je sais que c'est la trentaine de réponses à cette question, mais je pense que cela en vaut la peine, alors allez-y. Ceci est une solution réservée aux CSS avec les propriétés suivantes:

  • Il n'y a pas de retard au début et la transition ne s'arrête pas tôt. Dans les deux sens (développement et réduction), si vous spécifiez une durée de transition de 300 ms dans votre code CSS, la transition prend 300 ms, point.
  • Il effectue la transition de la hauteur réelle (à la différence de transform: scaleY(0)), donc il fait ce qu'il faut s'il y a du contenu après l'élément réductible.
  • Alors que (comme dans d'autres solutions) il sont nombres magiques (comme "choisissez une longueur supérieure à celle de votre boîte"), ce n'est pas fatal si votre hypothèse se termine en se trompant. La transition peut ne pas sembler étonnante dans ce cas, mais avant et après la transition, ce n’est pas un problème: dans l’état développé (height: auto), tout le contenu a toujours la hauteur correcte (contrairement à, par exemple, si vous choisissez un max-height qui se révèle être trop bas). Et dans l'état replié, la hauteur est égale à zéro comme il se doit.

Démo

Voici une démo avec trois éléments démontables, tous de hauteurs différentes, qui utilisent tous le même CSS. Vous voudrez peut-être cliquer sur "page entière" après avoir cliqué sur "exécuter l'extrait de code". Notez que le JavaScript ne fait que basculer la classe CSS collapsed CSS, aucune mesure n’est impliquée. (Vous pouvez faire cette démo exacte sans JavaScript en utilisant une case à cocher ou :target). Notez également que la partie du CSS responsable de la transition est assez courte et que le code HTML ne nécessite qu'un seul élément wrapper supplémentaire.

$(function () {
  $(".toggler").click(function () {
    $(this).next().toggleClass("collapsed");
    $(this).toggleClass("toggled"); // this just rotates the expander arrow
  });
});
.collapsible-wrapper {
  display: flex;
  overflow: hidden;
}
.collapsible-wrapper:after {
  content: '';
  height: 50px;
  transition: height 0.3s linear, max-height 0s 0.3s linear;
  max-height: 0px;
}
.collapsible {
  transition: margin-bottom 0.3s cubic-bezier(0, 0, 0, 1);
  margin-bottom: 0;
  max-height: 1000000px;
}
.collapsible-wrapper.collapsed > .collapsible {
  margin-bottom: -2000px;
  transition: margin-bottom 0.3s cubic-bezier(1, 0, 1, 1),
              visibility 0s 0.3s, max-height 0s 0.3s;
  visibility: hidden;
  max-height: 0;
}
.collapsible-wrapper.collapsed:after
{
  height: 0;
  transition: height 0.3s linear;
  max-height: 50px;
}

/* END of the collapsible implementation; the stuff below
   is just styling for this demo */

#container {
  display: flex;
  align-items: flex-start;
  max-width: 1000px;
  margin: 0 auto;
}  


.menu {
  border: 1px solid #ccc;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  margin: 20px;

  
}

.menu-item {
  display: block;
  background: linear-gradient(to bottom, #fff 0%,#eee 100%);
  margin: 0;
  padding: 1em;
  line-height: 1.3;
}
.collapsible .menu-item {
  border-left: 2px solid #888;
  border-right: 2px solid #888;
  background: linear-gradient(to bottom, #eee 0%,#ddd 100%);
}
.menu-item.toggler {
  background: linear-gradient(to bottom, #aaa 0%,#888 100%);
  color: white;
  cursor: pointer;
}
.menu-item.toggler:before {
  content: '';
  display: block;
  border-left: 8px solid white;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  width: 0;
  height: 0;
  float: right;
  transition: transform 0.3s ease-out;
}
.menu-item.toggler.toggled:before {
  transform: rotate(90deg);
}

body { font-family: sans-serif; font-size: 14px; }

*, *:after {
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="container">
  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

</div>

Comment ça marche?

Il y a en fait deux des transitions impliquées pour que cela se produise. L'un d'eux fait passer le margin-bottom de 0px (dans l'état développé) à -2000px dans l'état réduit (semblable à cette réponse ). Le 2000 ici est le premier chiffre magique, basé sur l'hypothèse que votre case ne sera pas plus haute que ça (2000 pixels semble être un choix raisonnable).

L'utilisation de la transition margin-bottom seule pose deux problèmes:

  • Si vous avez réellement une boîte supérieure à 2 000 pixels, un margin-bottom: -2000px ne cachera pas tout - il y aura des éléments visibles même dans le cas réduit. Ceci est une solution mineure que nous ferons plus tard.
  • Si, par exemple, la boîte a une hauteur de 1000 pixels et que votre transition dure 300 ms, la transition visible est déjà terminée au bout de 150 ms environ (ou, dans le sens opposé, commence 150ms en retard).

Ce deuxième problème est résolu lorsque la deuxième transition entre en jeu, et cette transition cible conceptuellement le wrapper minimum height ("conceptuellement" car nous n'utilisons pas réellement la propriété min-height pour cela; plus sur cela plus tard).

Voici une animation qui montre comment la combinaison de la transition de la marge inférieure et de la transition de hauteur minimale, toutes deux de durée égale, permet d'obtenir une transition combinée de hauteur totale à hauteur nulle ayant la même durée.

animation as described above

La barre de gauche montre comment la marge inférieure négative pousse le bas vers le haut, réduisant la hauteur visible. La barre du milieu montre comment la hauteur minimale garantit que, dans le cas d'une réduction, la transition ne se termine pas prématurément, et que dans le cas d'une expansion, la transition ne commence pas tard. La barre de droite montre comment la combinaison des deux provoque la transition de la hauteur maximale à la hauteur zéro dans le temps imparti.

Pour ma démo, j'ai choisi 50px comme valeur de hauteur minimale supérieure. C'est le deuxième nombre magique, et il devrait être inférieur à la hauteur de la boîte. 50px semble également raisonnable; il semble peu probable que vous souhaitiez très souvent créer un élément pliable ne dépassant pas 50 pixels de haut.

Comme vous pouvez le constater dans l'animation, la transition résultante est continue, mais elle n'est pas différenciable - au moment où la hauteur minimale est égale à la hauteur totale ajustée par la marge inférieure, il se produit un changement soudain de vitesse. Ceci est très visible dans l'animation car elle utilise une fonction de synchronisation linéaire pour les deux transitions et parce que toute la transition est très lente. Dans le cas réel (ma démo en haut), la transition ne prend que 300 ms et la transition de la marge inférieure n'est pas linéaire. J'ai joué avec de nombreuses fonctions de chronométrage différentes pour les deux transitions, et celles que j'ai eues avaient l'impression de fonctionner le mieux pour la plus grande variété de cas.

Deux problèmes restent à résoudre:

  1. le point d'en haut, où les cases de plus de 2 000 pixels de hauteur ne sont pas complètement cachées dans l'état replié,
  2. et le problème inverse, où, dans le cas non caché, les cases de hauteur inférieure à 50 pixels sont trop hautes, même lorsque la transition ne fonctionne pas, car la hauteur minimale les maintient à 50 pixels.

Nous résolvons le premier problème en donnant à l'élément conteneur un max-height: 0 dans le cas réduit, avec une transition 0s 0.3s. Cela signifie que ce n'est pas vraiment une transition, mais le max-height est appliqué avec un délai; cela ne s'applique qu'une fois la transition terminée. Pour que cela fonctionne correctement, nous devons également choisir un max-height numérique pour l’état opposé, non replié. Mais contrairement au cas de 2000px, où choisir un nombre trop important affecte la qualité de la transition, dans ce cas, cela n'a pas d'importance. Nous pouvons donc simplement choisir un nombre si élevé que nous savons qu'aucune hauteur ne nous approchera jamais. J'ai choisi un million de pixels. Si vous pensez avoir besoin de supporter un contenu d'une hauteur de plus d'un million de pixels, alors 1) je suis désolé et 2) ajoutez simplement quelques zéros.

Le deuxième problème est la raison pour laquelle nous n'utilisons pas réellement min-height pour la transition de hauteur minimale. Au lieu de cela, il existe un pseudo-élément ::after dans le conteneur avec un height qui passe de 50px à zéro. Cela a le même effet qu'un min-height: il ne laissera pas le conteneur se réduire en dessous de la hauteur que le pseudo-élément a actuellement. Mais comme nous utilisons height et non pas min-height, nous pouvons maintenant utiliser max-height (à nouveau appliqué avec un délai) pour définir la hauteur réelle du pseudo-élément à zéro une fois la transition terminée, en veillant à ce qu'au moins transition, même les petits éléments ont la bonne hauteur. Étant donné que min-height est plus fort que max-height, cela ne fonctionnerait pas si nous utilisions le min-height du conteneur à la place de height du pseudo-élément. Tout comme le max-height du paragraphe précédent, ce max-height a également besoin d'une valeur pour l'extrémité opposée de la transition. Mais dans ce cas, nous pouvons simplement choisir le 50px.

Testé dans Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge et IE11 (sauf un problème de mise en page avec flexbox avec ma démo qui ne m'a pas dérangé le débogage. ) et Safari (Mac, iOS). En parlant de flexbox, il devrait être possible de faire fonctionner cela sans utiliser de flexbox; En fait, je pense que vous pouvez faire fonctionner presque tout dans IE7 - à l'exception du fait que vous n'aurez pas de transitions CSS, ce qui en fait un exercice plutôt inutile.

77
balpha

Vous pouvez, avec un peu de jiggery-pokery non sémantique. Mon approche habituelle consiste à animer la hauteur d’une DIV externe ayant un seul enfant, une DIV sans style utilisée uniquement pour mesurer la hauteur du contenu.

function growDiv() {
  var growDiv = document.getElementById('grow');
  if (growDiv.clientHeight) {
    growDiv.style.height = 0;
  } else {
    var wrapper = document.querySelector('.measuringWrapper');
    growDiv.style.height = wrapper.clientHeight + "px";
  }
}
#grow {
  -moz-transition: height .5s;
  -ms-transition: height .5s;
  -o-transition: height .5s;
  -webkit-transition: height .5s;
  transition: height .5s;
  height: 0;
  overflow: hidden;
  outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
  <div class='measuringWrapper'>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
  </div>
</div>

On voudrait juste pouvoir se passer du .measuringWrapper et simplement régler la hauteur de la DIV sur auto et avoir cette animation, mais cela ne semble pas fonctionner (la hauteur est définie, mais aucune animation ne se produit).

function growDiv() {
  var growDiv = document.getElementById('grow');
  if (growDiv.clientHeight) {
    growDiv.style.height = 0;
  } else {
    growDiv.style.height = 'auto';
  }
}
#grow {
  -moz-transition: height .5s;
  -ms-transition: height .5s;
  -o-transition: height .5s;
  -webkit-transition: height .5s;
  transition: height .5s;
  height: 0;
  overflow: hidden;
  outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
</div>

Mon interprétation est qu'une hauteur explicite est nécessaire pour que l'animation s'exécute. Vous ne pouvez pas obtenir une animation sur la hauteur lorsque l'une ou l'autre hauteur (la hauteur de début ou de fin) vaut auto.

74
jhurshman

Une solution de contournement visuelle à l'animation de hauteur à l'aide de transitions CSS3 consiste à animer le remplissage.

Vous n'obtenez pas tout à fait l'effet d'effacement complet, mais jouer avec les valeurs de durée et de remplissage de la transition devrait vous rapprocher suffisamment. Si vous ne souhaitez pas définir explicitement hauteur/hauteur maximale, vous devriez obtenir ce que vous cherchez.

div {
    height: 0;
    overflow: hidden;
    padding: 0 18px;
    -webkit-transition: all .5s ease;
       -moz-transition: all .5s ease;
            transition: all .5s ease;
}
div.animated {
    height: auto;
    padding: 24px 18px;
}

http://jsfiddle.net/catharsis/n5XfG/17/ (Riffé à propos de stephband ci-dessus jsFiddle)

47
Catharsis

Ma solution consiste à faire passer max-height à la hauteur exacte du contenu pour une animation fluide et douce, puis à utiliser un rappel transitionEnd pour définir la hauteur maximale à 9999px afin que le contenu puisse être redimensionné librement.

var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed

// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
    if(content.hasClass('open')){
        content.css('max-height', 9999); // try setting this to 'none'... I dare you!
    }
});

$('#toggle').on('click', function(e){
    content.toggleClass('open closed');
    content.contentHeight = content.outerHeight();
    
    if(content.hasClass('closed')){
        
        // disable transitions & set max-height to content height
        content.removeClass('transitions').css('max-height', content.contentHeight);
        setTimeout(function(){
            
            // enable & start transition
            content.addClass('transitions').css({
                'max-height': 0,
                'opacity': 0
            });
            
        }, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
        // chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
        
    }else if(content.hasClass('open')){  
        
        content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
        content.css({
            'max-height': content.contentHeight,
            'opacity': 1
        });
        
    }
});
.transitions {
    transition: all 0.5s ease-in-out;
    -webkit-transition: all 0.5s ease-in-out;
    -moz-transition: all 0.5s ease-in-out;
}

body {
    font-family:Arial;
    line-height: 3ex;
}
code {
    display: inline-block;
    background: #fafafa;
    padding: 0 1ex;
}
#toggle {
    display:block;
    padding:10px;
    margin:10px auto;
    text-align:center;
    width:30ex;
}
#content {
    overflow:hidden;
    margin:10px;
    border:1px solid #666;
    background:#efefef;
    opacity:1;
}
#content .inner {
    padding:10px;
    overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
    <div class="inner">
        <h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
        <p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
        <p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
        <p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
    </div>
</div>

<br />

<button id="toggle">Challenge Accepted!</button>
45
Adam

La réponse acceptée fonctionne dans la plupart des cas, mais ne fonctionne pas bien lorsque votre div peut varier considérablement en hauteur. La vitesse d'animation ne dépend pas de la hauteur réelle du contenu et elle peut sembler agitée.

Vous pouvez toujours exécuter l'animation réelle avec CSS, mais vous devez utiliser JavaScript pour calculer la hauteur des éléments, au lieu d'essayer d'utiliser auto. JQuery n'est pas requis, mais vous devrez peut-être modifier cela un peu si vous voulez la compatibilité (fonctionne dans la dernière version de Chrome :)).

window.toggleExpand = function(element) {
    if (!element.style.height || element.style.height == '0px') { 
        element.style.height = Array.prototype.reduce.call(element.childNodes, function(p, c) {return p + (c.offsetHeight || 0);}, 0) + 'px';
    } else {
        element.style.height = '0px';
    }
}
#menu #list {
    height: 0px;
    transition: height 0.3s ease;
    background: #d5d5d5;
    overflow: hidden;
}
<div id="menu">
    <input value="Toggle list" type="button" onclick="toggleExpand(document.getElementById('list'));">
    <ul id="list">
        <!-- Works well with dynamically-sized content. -->
        <li>item</li>
        <li><div style="height: 100px; width: 100px; background: red;"></div></li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>
34
Oleg Vaskevich

Utilisez max-height avec un relâchement et un délai de transition différents pour chaque état.

HTML:

<a href="#" id="trigger">Hover</a>
<ul id="toggled">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
<ul>

CSS:

#toggled{
    max-height: 0px;
    transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}

#trigger:hover + #toggled{
    max-height: 9999px;
    transition-timing-function: cubic-bezier(0.5, 0, 1, 0); 
    transition-delay: 0s;
}

Voir exemple: http://jsfiddle.net/0hnjehjc/1/

26
malihu

Aucune valeur codée en dur.

Pas de JavaScript.

Aucune approximation.

L'astuce consiste à utiliser un div masqué et dupliqué pour que le navigateur comprenne la signification de 100%.

Cette méthode convient chaque fois que vous pouvez dupliquer le DOM de l'élément que vous souhaitez animer.

.outer {
  border: dashed red 1px;
  position: relative;
}

.dummy {
  visibility: hidden;
}

.real {
  position: absolute;
  background: yellow;
  height: 0;
  transition: height 0.5s;
  overflow: hidden;
}

.outer:hover>.real {
  height: 100%;
}
Hover over the box below:
<div class="outer">
  <!-- The actual element that you'd like to animate -->
  <div class="real">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
  <!-- An exact copy of the element you'd like to animate. -->
  <div class="dummy" aria-hidden="true">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
</div>
26
Vivek Maharajh

Il a été peu fait mention de la propriété Element.scrollHeight qui peut être utile ici et peut toujours être utilisée avec une transition CSS pure. La propriété contient toujours la hauteur "complète" d'un élément, indépendamment du fait que son contenu déborde en raison de la hauteur réduite (par exemple, _height: 0_).

En tant que tel, pour un élément _height: 0_ (effectivement totalement réduit), sa hauteur "normale" ou "complète" est toujours disponible par le biais de sa valeur scrollHeight (invariablement un pixel longueur).

Pour un tel élément, en supposant que la transition est déjà établie comme par ex. (en utilisant ul selon la question d'origine):

_ul {
    height: 0;
    transition: height 1s; /* An example transition. */
}
_

Nous pouvons déclencher le "développement" animé de hauteur désirée, en utilisant uniquement CSS, avec quelque chose comme ce qui suit (en supposant que ul variable fait référence à la liste):

_ul.style.height = ul.scrollHeight + "px";
_

C'est ça. Si vous devez réduire la liste, l'une des deux instructions suivantes suffira:

_ul.style.height = "0";
ul.style.removeProperty("height");
_

Mon cas d'utilisation particulier tournait autour de l'animation de listes de longueurs inconnues et souvent considérables. Je ne me sentais donc pas à l'aise de choisir une spécification arbitraire "assez grande" height ou _max-height_ et de risquer un contenu tronqué soudainement besoin de faire défiler (si _overflow: auto_, par exemple). De plus, l’assouplissement et la synchronisation sont interrompus avec les solutions basées sur _max-height_, car la hauteur utilisée peut atteindre sa valeur maximale beaucoup plus tôt que cela prendrait pour que _max-height_ atteigne _9999px_. Et à mesure que les résolutions d'écran augmentent, des longueurs de pixel telles que _9999px_ me laissent un mauvais goût. Cette solution particulière résout le problème de manière élégante, à mon avis.

Enfin, nous espérons enfin que les futures révisions de CSS s’adresseront au besoin des auteurs de faire ce genre de choses avec encore plus d’élégance - revisiter la notion de "calculés" par rapport aux valeurs "utilisées" et "résolues" et examiner si les transitions doivent s’appliquer aux calculs calculés. valeurs, y compris les transitions avec width et height (qui bénéficient actuellement d’un traitement spécial).

24
amn

Alors que je poste ceci, il y a déjà plus de 30 réponses, mais je sens que ma réponse s’améliore sur celle déjà réponse acceptée par jake.

Je n’étais pas satisfait du problème qui découle du simple fait d’utiliser max-height et des transitions CSS3, car, comme de nombreux commentateurs l’ont noté, vous devez définir votre valeur max-height très proche de la hauteur réelle. retard. Voir ceci JSFiddle pour un exemple de ce problème.

Pour contourner ce problème (tout en n'utilisant toujours pas de JavaScript), j'ai ajouté un autre élément HTML transformant la valeur transform: translateY CSS.

Cela signifie que max-height et translateY sont utilisés: max-height permet à l'élément d'abaisser les éléments situés en dessous, tandis que translateY donne l'effet "instant" souhaité. Le problème avec max-height existe toujours, mais son effet est atténué. Cela signifie que vous pouvez définir une hauteur beaucoup plus grande pour votre valeur max-height et vous en soucier moins.

L'avantage général est que lors de la transition (l'effondrement), l'utilisateur voit immédiatement l'animation translateY. Le temps que prend max-height n'a donc pas d'importance.

Solution comme violon

body {
  font-family: sans-serif;
}

.toggle {
  position: relative;
  border: 2px solid #333;
  border-radius: 3px;
  margin: 5px;
  width: 200px;
}

.toggle-header {
  margin: 0;
  padding: 10px;
  background-color: #333;
  color: white;
  text-align: center;
  cursor: pointer;
}

.toggle-height {
  background-color: tomato;
  overflow: hidden;
  transition: max-height .6s ease;
  max-height: 0;
}

.toggle:hover .toggle-height {
  max-height: 1000px;
}

.toggle-transform {
  padding: 5px;
  color: white;
  transition: transform .4s ease;
  transform: translateY(-100%);
}

.toggle:hover .toggle-transform {
  transform: translateY(0);
}
<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>

<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>
21
Andrew Messier

Ok, je pense donc avoir une réponse très simple ... non max-height, utilise relative positionnement, fonctionne sur li éléments, & est du pur CSS. Je n'ai testé que sur Firefox, mais à en juger par le CSS, cela devrait fonctionner sur tous les navigateurs.

FIDDLE: http://jsfiddle.net/n5XfG/2596/

CSS

.wrap { overflow:hidden; }

.inner {
            margin-top:-100%;
    -webkit-transition:margin-top 500ms;
            transition:margin-top 500ms;
}

.inner.open { margin-top:0px; }

HTML

<div class="wrap">
    <div class="inner">Some Cool Content</div>
</div>
17
VIDesignz

EDIT: Faites défiler pour la réponse mise à jour
Je faisais une liste déroulante et j'ai vu ce message ... beaucoup de réponses différentes, mais je décide de partager ma liste déroulante aussi, ... Ce n'est pas parfait, mais au moins, il utilisera uniquement des fichiers CSS pour le menu déroulant. ! J'utilise transform: translateY (y) pour transformer la liste en vue ...
Vous pouvez voir plus dans le test
http://jsfiddle.net/BVEpc/4/
J'ai placé div derrière chaque li parce que ma liste déroulante vient d'en haut et pour leur montrer correctement que c'était nécessaire, mon code div est:

#menu div {
    transition: 0.5s 1s;
    z-index:-1;
    -webkit-transform:translateY(-100%);
    -webkit-transform-Origin: top;
}

et le survol est:

#menu > li:hover div {
    transition: 0.5s;
    -webkit-transform:translateY(0);
}

et parce que ul height est réglé sur le contenu, il peut recouvrir le contenu de votre corps, c'est pourquoi j'ai fait ceci pour ul:

 #menu ul {
    transition: 0s 1.5s;
    visibility:hidden;
    overflow:hidden;
}

et survolez:

#menu > li:hover ul {
     transition:none;
     visibility:visible;
}

la deuxième fois après la transition est un délai et il sera caché après la fermeture de ma liste déroulante ...
J'espère que plus tard, quelqu'un en tirera profit.

EDIT: Je n'arrive pas à croire que ppl utilise réellement ce prototype! ce menu déroulant est seulement pour un sous-menu et c'est tout !! J'ai mis à jour un meilleur qui peut avoir deux sous-menu pour les deux directions ltr et rtl avec le support IE 8.
Fiddle for LTR
Fiddle for RTL
J'espère que cela sera utile dans le futur.

14
Sijav

Vous pouvez passer de height: 0 à height: auto, à condition que vous fournissiez également min-height et max-height.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
11
Stuart Badminton

Solution Flexbox

Avantages:

  • facile
  • pas de JS
  • transition en douceur

Les inconvénients:

  • l'élément doit être placé dans un conteneur flexible à hauteur fixe

La façon dont cela fonctionne est de toujours avoir flex-base: auto sur l'élément avec le contenu, et la transition flex-grow et flex-shrink à la place.

Edit: JS Fiddle amélioré, inspiré de l’interface Xbox One.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  transition: 0.25s;
  font-family: monospace;
}

body {
  margin: 10px 0 0 10px;
}

.box {
  width: 150px;
  height: 150px;
  margin: 0 2px 10px 0;
  background: #2d333b;
  border: solid 10px #20262e;
  overflow: hidden;
  display: inline-flex;
  flex-direction: column;
}

.space {
  flex-basis: 100%;
  flex-grow: 1;
  flex-shrink: 0;    
}

p {
  flex-basis: auto;
  flex-grow: 0;
  flex-shrink: 1;
  background: #20262e;
  padding: 10px;
  width: 100%;
  text-align: left;
  color: white;
}

.box:hover .space {
  flex-grow: 0;
  flex-shrink: 1;
}
  
.box:hover p {
  flex-grow: 1;
  flex-shrink: 0;    
}
<div class="box">
  <div class="space"></div>
  <p>
    Super Metroid Prime Fusion
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Resident Evil 2 Remake
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Yolo The Game
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Final Fantasy 7 Remake + All Additional DLC + Golden Tophat
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    DerpVille
  </p>
</div>

JS Fiddle

10
Lee Comstock

Je pense avoir trouvé une solution vraiment solide

D'ACCORD! Je sais que ce problème est aussi vieux qu’Internet mais je pense avoir une solution que j’ai transformée en plugin appelé mutant-transition . Ma solution définit les attributs style="" pour les éléments suivis chaque fois qu'une modification est apportée au DOM. Le résultat final est que vous pouvez utiliser de bonnes vieilles CSS pour vos transitions et ne pas utiliser de correctifs hacky ou de javascript spécial. La seule chose à faire est de définir ce que vous souhaitez suivre sur l'élément en question à l'aide de data-mutant-attributes="X".

<div data-mutant-attributes="height">                                                                      
        This is an example with mutant-transition                                                                                                          
    </div>

C'est ça! Cette solution utilise MutationObserver pour suivre les modifications dans le DOM. Pour cette raison, vous n'avez pas vraiment besoin de configurer quoi que ce soit ou d'utiliser du javascript pour animer manuellement des choses. Les modifications sont suivies automatiquement. Cependant, comme il utilise MutationObserver, cela ne fera que passer dans IE11 +.

Fiddles!

9
JonTroncoso

Voici un moyen de passer de n'importe quelle hauteur de départ, y compris 0, à automatique (taille complète et flexible) sans nécessiter de code fixe par nœud ni de code d'utilisateur à initialiser: https: // github .com/csuwildcat/transition-auto . C’est fondamentalement le Saint Graal de ce que vous voulez, je crois -> http://codepen.io/csuwldcat/pen/kwsdF . Insérez simplement le fichier JS suivant dans votre page. Il vous suffit ensuite d'ajouter/de supprimer un attribut booléen unique - reveal="" - des nœuds que vous souhaitez développer et contracter.

Voici tout ce que vous devez faire en tant qu'utilisateur, une fois que vous avez inclus le bloc de code situé sous l'exemple de code:

/*** Nothing out of the ordinary in your styles ***/
<style>
    div {
        height: 0;
        overflow: hidden;
        transition: height 1s;
    }
</style>

/*** Just add and remove one attribute and transition to/from auto! ***/

<div>
    I have tons of content and I am 0px in height you can't see me...
</div>

<div reveal>
     I have tons of content and I am 0px in height you can't see me...
     but now that you added the 'reveal' attribute, 
     I magically transitioned to full height!...
</div>

Voici le bloc de code à inclure dans votre page, après cela, tout est en sauce:

Déposez ce fichier JS dans votre page - Tout fonctionne Just Works ™

/ * Code de hauteur: auto; transition * /

(function(doc){

/* feature detection for browsers that report different values for scrollHeight when an element's overflow is hidden vs visible (Firefox, IE) */
var test = doc.documentElement.appendChild(doc.createElement('x-reveal-test'));
    test.innerHTML = '-';
    test.style.cssText = 'display: block !important; height: 0px !important; padding: 0px !important; font-size: 0px !important; border-width: 0px !important; line-height: 1px !important; overflow: hidden !important;';
var scroll = test.scrollHeight || 2;
doc.documentElement.removeChild(test);

var loading = true,
    numReg = /^([0-9]*\.?[0-9]*)(.*)/,
    skipFrame = function(fn){
      requestAnimationFrame(function(){
        requestAnimationFrame(fn);
      });
    },
    /* 2 out of 3 uses of this function are purely to work around Chrome's catastrophically busted implementation of auto value CSS transitioning */
    revealFrame = function(el, state, height){
        el.setAttribute('reveal-transition', 'frame');
        el.style.height = height;
        skipFrame(function(){
            el.setAttribute('reveal-transition', state);
            el.style.height = '';
        });
    },
    transitionend = function(e){
      var node = e.target;
      if (node.hasAttribute('reveal')) {
        if (node.getAttribute('reveal-transition') == 'running') revealFrame(node, 'complete', '');
      } 
      else {
        node.removeAttribute('reveal-transition');
        node.style.height = '';
      }
    },
    animationstart = function(e){
      var node = e.target,
          name = e.animationName;   
      if (name == 'reveal' || name == 'unreveal') {

        if (loading) return revealFrame(node, 'complete', 'auto');

        var style = getComputedStyle(node),
            offset = (Number(style.paddingTop.match(numReg)[1])) +
                     (Number(style.paddingBottom.match(numReg)[1])) +
                     (Number(style.borderTopWidth.match(numReg)[1])) +
                     (Number(style.borderBottomWidth.match(numReg)[1]));

        if (name == 'reveal'){
          node.setAttribute('reveal-transition', 'running');
          node.style.height = node.scrollHeight - (offset / scroll) + 'px';
        }
        else {
            if (node.getAttribute('reveal-transition') == 'running') node.style.height = '';
            else revealFrame(node, 'running', node.scrollHeight - offset + 'px');
        }
      }
    };

doc.addEventListener('animationstart', animationstart, false);
doc.addEventListener('MSAnimationStart', animationstart, false);
doc.addEventListener('webkitAnimationStart', animationstart, false);
doc.addEventListener('transitionend', transitionend, false);
doc.addEventListener('MSTransitionEnd', transitionend, false);
doc.addEventListener('webkitTransitionEnd', transitionend, false);

/*
    Batshit readyState/DOMContentLoaded code to dance around Webkit/Chrome animation auto-run weirdness on initial page load.
    If they fixed their code, you could just check for if(doc.readyState != 'complete') in animationstart's if(loading) check
*/
if (document.readyState == 'complete') {
    skipFrame(function(){
        loading = false;
    });
}
else document.addEventListener('DOMContentLoaded', function(e){
    skipFrame(function(){
        loading = false;
    });
}, false);

/* Styles that allow for 'reveal' attribute triggers */
var styles = doc.createElement('style'),
    t = 'transition: none; ',
    au = 'animation: reveal 0.001s; ',
    ar = 'animation: unreveal 0.001s; ',
    clip = ' { from { opacity: 0; } to { opacity: 1; } }',
    r = 'keyframes reveal' + clip,
    u = 'keyframes unreveal' + clip;

styles.textContent = '[reveal] { -ms-'+ au + '-webkit-'+ au +'-moz-'+ au + au +'}' +
    '[reveal-transition="frame"] { -ms-' + t + '-webkit-' + t + '-moz-' + t + t + 'height: auto; }' +
    '[reveal-transition="complete"] { height: auto; }' +
    '[reveal-transition]:not([reveal]) { -webkit-'+ ar +'-moz-'+ ar + ar +'}' +
    '@-ms-' + r + '@-webkit-' + r + '@-moz-' + r + r +
    '@-ms-' + u +'@-webkit-' + u + '@-moz-' + u + u;

doc.querySelector('head').appendChild(styles);

})(document);

/ * Code pour DEMO * /

    document.addEventListener('click', function(e){
      if (e.target.nodeName == 'BUTTON') {
        var next = e.target.nextElementSibling;
        next.hasAttribute('reveal') ? next.removeAttribute('reveal') : next.setAttribute('reveal', '');
      }
    }, false);
7
csuwldcat

La réponse de Jake pour animer la hauteur maximale est excellente, mais j'ai trouvé le retard causé par la configuration d'une grande hauteur maximale gênant.

On pourrait déplacer le contenu compressible dans une div interne et calculer la hauteur maximale en obtenant la hauteur de la div interne (via JQuery, ce serait le outerHeight ()).

$('button').bind('click', function(e) { 
  e.preventDefault();
  w = $('#outer');
  if (w.hasClass('collapsed')) {
    w.css({ "max-height": $('#inner').outerHeight() + 'px' });
  } else {
    w.css({ "max-height": "0px" });
  }
  w.toggleClass('collapsed');
});

Voici un lien jsfiddle: http://jsfiddle.net/pbatey/duZpT

Voici un jsfiddle avec la quantité minimale absolue de code nécessaire: http://jsfiddle.net/8ncjjxh8/

7
pbatey

En développant la réponse de @ jake, la transition atteindra la valeur de hauteur maximale, ce qui provoquera une animation extrêmement rapide. Si vous définissez les transitions pour les deux: en survol et en arrêt, vous pouvez alors contrôler un peu plus la vitesse folle.

Donc, le li: survol se produit lorsque la souris entre dans l'état, puis la transition sur la propriété non survolée sera le point de départ de la souris.

J'espère que cela vous sera utile.

par exemple:

.sidemenu li ul {
   max-height: 0px;
   -webkit-transition: all .3s ease;
   -moz-transition: all .3s ease;
   -o-transition: all .3s ease;
   -ms-transition: all .3s ease;
   transition: all .3s ease;
}
.sidemenu li:hover ul {
    max-height: 500px;
    -webkit-transition: all 1s ease;
   -moz-transition: all 1s ease;
   -o-transition: all 1s ease;
   -ms-transition: all 1s ease;
   transition: all 1s ease;
}
/* Adjust speeds to the possible height of the list */

Voici un violon: http://jsfiddle.net/BukwJ/

6
jamie

Je me rends compte que ce fil vieillit, mais il figure en bonne place dans certaines recherches Google. Je pense donc qu'il vaut la peine de le mettre à jour.

Vous venez également d'obtenir/définir la propre hauteur de l'élément:

var load_height = document.getElementById('target_box').clientHeight;
document.getElementById('target_box').style.height = load_height + 'px';

Vous devez sauvegarder ce code Javascript immédiatement après la balise de fermeture de target_box dans une balise de script en ligne.

5
Charley

J'ai pu faire ça. J'ai un .child & un .parent div. La div de l'enfant s'adapte parfaitement à la largeur/hauteur du parent avec le positionnement absolute. J'anime ensuite la propriété translate en Push it Y value 100%. Son animation très fluide, pas de problèmes ou des inconvénients comme toute autre solution ici.

Quelque chose comme ça, pseudo code

.parent{ position:relative; overflow:hidden; } 
/** shown state */
.child {
  position:absolute;top:0;:left:0;right:0;bottom:0;
  height: 100%;
  transition: transform @overlay-animation-duration ease-in-out;
  .translate(0, 0);
}

/** Animate to hidden by sliding down: */
.child.slidedown {
  .translate(0, 100%); /** Translate the element "out" the bottom of it's .scene container "mask" so its hidden */
}

Vous devez spécifier un height sur .parent, dans px, % ou laisser auto. Cette div masque alors la .child div lorsqu'elle glisse vers le bas.

5
Josh Ribakoff

Voici une solution que je viens d'utiliser en combinaison avec jQuery. Cela fonctionne pour la structure HTML suivante:

<nav id="main-nav">
    <ul>
        <li>
            <a class="main-link" href="yourlink.html">Link</a>
            <ul>
                <li><a href="yourlink.html">Sub Link</a></li>
            </ul>
        </li>
    </ul>
</nav>

et la fonction:

    $('#main-nav li ul').each(function(){
        $me = $(this);

        //Count the number of li elements in this UL
        var liCount = $me.find('li').size(),
        //Multiply the liCount by the height + the margin on each li
            ulHeight = liCount * 28;

        //Store height in the data-height attribute in the UL
        $me.attr("data-height", ulHeight);
    });

Vous pouvez ensuite utiliser une fonction de clic pour définir et supprimer la hauteur à l'aide de css()

$('#main-nav li a.main-link').click(function(){
    //Collapse all submenus back to 0
    $('#main-nav li ul').removeAttr('style');

    $(this).parent().addClass('current');

    //Set height on current submenu to it's height
    var $currentUl = $('li.current ul'),
        currentUlHeight = $currentUl.attr('data-height');
})

CSS:

#main-nav li ul { 
    height: 0;
    position: relative;
    overflow: hidden;
    opacity: 0; 
    filter: alpha(opacity=0); 
    -ms-filter: "alpha(opacity=0)";
    -khtml-opacity: 0; 
    -moz-opacity: 0;
    -webkit-transition: all .6s ease-in-out;
    -moz-transition: all .6s ease-in-out;
    -o-transition: all .6s ease-in-out;
    -ms-transition: all .6s ease-in-out;
    transition: all .6s ease-in-out;
}

#main-nav li.current ul {
    opacity: 1.0; 
    filter: alpha(opacity=100); 
    -ms-filter: "alpha(opacity=100)";
    -khtml-opacity: 1.0; 
    -moz-opacity: 1.0;
}

.ie #main-nav li.current ul { height: auto !important }

#main-nav li { height: 25px; display: block; margin-bottom: 3px }
4
j-man86

J'ai récemment effectué la transition de max-height sur les éléments li plutôt que sur l'habillage ul.

Le raisonnement est que le retard pour les petits max-heights est beaucoup moins perceptible (voire pas du tout) par rapport aux grands max-heights, et je peux aussi définir ma valeur max-height par rapport à font-size de la li plutôt qu'un nombre énorme arbitraire en utilisant ems ou rems.

Si ma taille de police est 1rem, je vais définir mon max-height sur quelque chose comme 3rem (pour permettre le texte enveloppé). Vous pouvez voir un exemple ici:

http://codepen.io/mindfullsilence/pen/DtzjE

4
mindfullsilence

Je comprends la question demande une solution sans JavaScript. Mais pour ceux que ça intéresse, voici ma solution en utilisant juste un peu de JS.

ok, donc le css de l'élément dont la hauteur changera par défaut est défini sur height: 0; et lorsqu'il est ouvert height: auto;. Il a aussi transition: height .25s ease-out;. Mais bien sûr, le problème est qu’il ne passera pas de ou vers height: auto;

Donc, ce que j’ai fait, lors de l’ouverture ou de la fermeture, définissez la hauteur sur la propriété scrollHeight de l’élément. Ce nouveau style en ligne aura une spécificité plus élevée et remplacera height: auto; et height: 0; et la transition sera exécutée.

Lors de l'ouverture, j'ajoute un écouteur d'événement transitionend qui s'exécutera une seule fois, puis supprime le style en ligne et le redéfinit sur height: auto;, ce qui permettra à l'élément de redimensionner si nécessaire, comme dans cet exemple plus complexe avec des sous-menus. https://codepen.io/ninjabonsai/pen/GzYyVe

Lors de la fermeture, je supprime le style en ligne juste après le prochain cycle boucle d'événement en utilisant setTimeout sans délai. Cela signifie que height: auto; est temporairement remplacé, ce qui permet de revenir à height 0;.

const showHideElement = (element, open) => {
  element.style.height = element.scrollHeight + 'px';
  element.classList.toggle('open', open);

  if (open) {
    element.addEventListener('transitionend', () => {
      element.style.removeProperty('height');
    }, {
      once: true
    });
  } else {
    window.setTimeout(() => {
      element.style.removeProperty('height');
    });
  }
}

const menu = document.body.querySelector('#menu');
const list = document.body.querySelector('#menu > ul')

menu.addEventListener('mouseenter', () => showHideElement(list, true));
menu.addEventListener('mouseleave', () => showHideElement(list, false));
#menu>ul {
  height: 0;
  overflow: hidden;
  background-color: #999;
  transition: height .25s ease-out;
}

#menu>ul.open {
  height: auto;
}
<div id="menu">
  <a>hover me</a>
  <ul>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
  </ul>
</div>
3
shunryu111

Je n'ai pas tout lu en détail mais j'ai eu ce problème récemment et j'ai fait ce qui suit:

div.class{
   min-height:1%;
   max-height:200px;
   -webkit-transition: all 0.5s ease;
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   -webkit-transition: all 0.5s ease;
   transition: all 0.5s ease;
   overflow:hidden;
}

div.class:hover{
   min-height:100%;
   max-height:3000px;
}

Cela vous permet d’avoir une div qui affiche un contenu d’une hauteur maximale de 200px et en vol stationnaire, sa taille est au moins aussi élevée que l’ensemble du contenu de la div. La Div ne devient pas 3000px mais 3000px est la limite que j'impose. Assurez-vous d'avoir la transition sur le non: survol, sinon vous pourriez obtenir un rendu étrange. De cette façon, le: hover hérite du non: hover.

La transition ne fonctionne pas de px à% ou à auto. Vous devez utiliser la même unité de mesure. Cela fonctionne bien pour moi. Utiliser HTML5 le rend parfait ....

Rappelez-vous qu'il y a toujours un moyen de contourner ...; )

J'espère que quelqu'un trouve cela utile

3
work.stella84

Ce n'est pas exactement une "solution" au problème, mais plutôt une solution de contournement. Cela fonctionne uniquement comme écrit avec du texte, mais peut être modifié pour fonctionner avec d'autres éléments si nécessaire, j'en suis sûr.

.originalContent {
    font-size:0px;
    transition:font-size .2s ease-in-out;
}
.show { /* class to add to content */
    font-size:14px;
}

Voici un exemple: http://codepen.io/overthemike/pen/wzjRKa

Pour l’essentiel, vous définissez la taille de la police sur 0 et effectuez la transition à la place de la hauteur, de la hauteur maximale ou de scaleY (), etc. à un rythme suffisamment rapide pour obtenir la hauteur qui vous convient. Transformer la hauteur réelle avec CSS en auto n'est pas actuellement possible, mais transformer le contenu en est également, d'où la transition taille-police.

  • Remarque - il y a IS javascript dans le codepen, mais son seul but est d'ajouter/de supprimer des classes css au clic pour l'accordéon. Cela peut être fait avec des boutons radio cachés, mais je n’étais pas concentré sur cela, juste sur la transformation de la hauteur.
3
Mike S.

Il semble n'y avoir aucune solution appropriée. L'approche max-height est assez bonne mais ne fonctionne pas bien pour la phase de masquage - il y aura un retard notable à moins que vous ne connaissiez la hauteur du contenu.

Je pense que le meilleur moyen est d’utiliser max-height mais niquement pour la phase d’émission. Et ne pas utiliser d'animation pour se cacher. Dans la plupart des cas, cela ne devrait pas être crucial.

max-height devrait être défini sur une valeur assez énorme pour garantir l'adéquation du contenu. La vitesse d'animation peut être contrôlée en utilisant transition duration (speed = max-height / duration). La vitesse ne dépend pas de la taille du contenu. Le temps nécessaire pour afficher tout le contenu dépend de sa taille.

document.querySelector("button").addEventListener(
  "click", 
  function(){
    document.querySelector("div").classList.toggle("hide");
  }
)
div {    
    max-height: 20000px;
    transition: max-height 3000ms;
    overflow-y: hidden;
}

.hide {
    max-height: 0;
    transition: none;
}
<button>Toggle</button>
<div class="hide">Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. 
</div>
2
Andrej

La solution de hauteur maximale de Jake fonctionne bien si la valeur de hauteur maximale codée en dur fournie n’est pas beaucoup plus grande que la hauteur réelle (car sinon, il y a des retards et des problèmes de synchronisation indésirables). D'autre part, si la valeur codée en dur n'est pas accidentellement supérieure à la hauteur réelle, l'élément ne s'ouvrira pas complètement.

La solution CSS uniquement suivante requiert également une taille codée en dur qui devrait être supérieure à la plupart des tailles réelles. Cependant, cette solution fonctionne également si, dans certaines situations, la taille réelle est supérieure à la taille codée en dur. Dans ce cas, la transition peut sauter un peu, mais elle ne laissera jamais un élément partiellement visible. Cette solution pourrait donc également être utilisée pour un contenu inconnu, par exemple. à partir d'une base de données, où vous savez que le contenu n'est généralement pas plus grand que x pixels, mais il existe des exceptions.

L'idée est d'utiliser une valeur négative pour margin-bottom (ou margin-top pour une animation légèrement différente) et de placer l'élément content dans un élément central avec overflow: hidden. La marge négative de l'élément de contenu réduit donc la hauteur de l'élément central.

Le code suivant utilise une transition entre margin-bottom de -150px et 0px. Cela seul fonctionne bien tant que l'élément content n'est pas supérieur à 150px. De plus, il utilise une transition sur max-height pour l'élément central de 0px à 100%. Ceci masque finalement l'élément central si l'élément de contenu est supérieur à 150px. Pour max-height, la transition est simplement utilisée pour retarder son application d'une seconde lors de la fermeture, mais pas pour un effet visuel fluide (et peut donc aller de 0px à 100%).

CSS:

.content {
  transition: margin-bottom 1s ease-in;
  margin-bottom: -150px;
}
.outer:hover .middle .content {
  transition: margin-bottom 1s ease-out;
  margin-bottom: 0px
}
.middle {
  overflow: hidden;
  transition: max-height .1s ease 1s;
  max-height: 0px
}
.outer:hover .middle {
  transition: max-height .1s ease 0s;
  max-height: 100%
}

HTML:

<div class="outer">
  <div class="middle">
    <div class="content">
      Sample Text
      <br> Sample Text
      <br> Sample Text
      <div style="height:150px">Sample Test of height 150px</div>
      Sample Text
    </div>
  </div>
  Hover Here
</div>

La valeur pour le bas de la marge doit être négative et aussi proche que possible de la hauteur réelle de l'élément de contenu. Si elle (valeur d'absoute) est plus grande, les problèmes de délai et de synchronisation sont similaires à ceux des solutions de hauteur maximale, qui peuvent toutefois être limitées tant que la taille codée en dur n'est pas beaucoup plus grande que la taille réelle. Si la valeur absolue de margin-bottom est inférieure à la hauteur réelle, la transition saute un peu. Dans tous les cas, après la transition, l'élément de contenu est soit totalement affiché, soit complètement supprimé.

Pour plus de détails, consultez mon article de blog http://www.taccgl.org/blog/css_transition_display.html#combined_height

2
Helmut Emmelmann

Définissez la hauteur sur auto et réglez la hauteur maximale.

Testé sur Chrome v17

div {
  position: absolute;
  width:100%;
  bottom:0px;
  left:0px;

  background:#333;
  color: #FFF;

  max-height:100%; /**/
  height:auto; /**/

  -webkit-transition: all 0.2s ease-in-out;
  -moz-transition: all 0.2s ease-in-out;
  -o-transition: all 0.2s ease-in-out;
  -ms-transition: all 0.2s ease-in-out;
  transition: all 0.2s ease-in-out;
}

.close {
  max-height:0%; /**/
}
1
will Farrell

LITTLE JAVASCRIPT + SCSS SOLUTION

J'utilise généralement un point de vue différent et un (très) petit javascript. La chose est:

  • ce que nous voulons vraiment, c'est changer de hauteur

  • La hauteur est la somme de tous les éléments de la liste dans le sous-menu.

  • Nous connaissons généralement la hauteur d'un élément de la liste, car nous l'appelons

Ma solution s’applique donc aux sous-menus "normaux" où les noms d’éléments ne comportent qu’une rangée. Quoi qu’il en soit, avec un peu plus de js, on pourrait accueillir même plus d’un nom de rangée.

En gros, je compte simplement les éléments de sous-menus et applique des classes spécifiques en conséquence. Puis passez le ballon à (s) css. Donc, par exemple:

var main_menu = $('.main-menu');
var submenus = $('.main-menu').find('.submenu');
submenus.each(function(index,item){
   var i = $(item);
   i.addClass('has-' + i.find('li').length + '-children');
});

Vous pouvez évidemment utiliser n'importe quelle classe/sélecteur. À ce stade, nous avons des sous-menus comme celui-ci:

<ul class="submenu has-3-children">
   <li></li>
   <li></li>
   <li></li>
</ul>

Et nos css aiment ça:

.submenu{
   //your styles [...]
   height:0;
   overflow:hidden;
   transition: all 200ms ease-in-out; //assume Autoprefixer is used
}

Nous aurons aussi des variables scss comme celles-ci (exemple arbitraire):

$sub_item_height:30px;
$sub_item_border:2px;

À ce stade, en supposant que les éléments de menu principal ouverts obtiennent une classe du type 'ouvert' ou similaire (vos implémentations ..), nous pouvons faire quelque chose comme ceci:

//use a number of children reasonably high so it won't be overcomed by real buttons
.main-menu .opened .submenu{
   &.has-1-children{ height:   $sub_item_height*1  + $sub_item_border*1;  }
   &.has-2-children{ height:   $sub_item_height*2  + $sub_item_border*2;  }
   &.has-3-children{ height:   $sub_item_height*3  + $sub_item_border*3;  }
   //and so on....
}

Ou, pour raccourcir:

.main-menu .opened .submenu{
   @for $i from 1 through 12{//12 is totally arbitrary
      &.has-#{$i}-children { height: $menu_item_height * $i + $menu_item_border * $i; }
   }
}

La plupart du temps, cela fera l'affaire. J'espère que ça aide!

0
Luca Reghellin

C'est un problème régulier que j'ai résolu comme ça

http://jsfiddle.net/ipeshev/d1dfr0jz/

Essayez de définir le délai de l'état fermé à un nombre négatif et jouez un peu avec la valeur. Vous verrez la différence.Il peut être fait presque mentir l'œil humain;).

Cela fonctionne dans les principaux navigateurs, mais assez bien pour moi. C'est étrange mais donne des résultats.

.expandable {
    max-height: 0px;
    overflow: hidden;
    transition: all 1s linear -0.8s;
}

button:hover ~ .expandable {
    max-height: 9000px;
    transition: all 1s ease-in-out;
}
0
Ivan Peshev