css - working - transition width auto




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

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

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

Si je le fais de la height: 40px; à height: auto; , alors il glissera jusqu’à height: 0; , puis saute soudainement à 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>


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 CSS uniquement 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 fait la transition à la hauteur réelle (contrairement à transform: scaleY(0) ), donc il fait ce qu'il faut s'il y a du contenu après l'élément réductible.
  • Bien que (comme dans d’autres solutions), il existe des nombres magiques (comme "choisir une longueur supérieure à celle de votre boîte"), ce n’est pas fatal si votre hypothèse est fausse. Dans ce cas, la transition peut ne pas sembler étonnante, 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 s’avère trop basse). 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 réduite, 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?

Pour ce faire, il y a deux transitions. L'un d'eux fait la transition du margin-bottom de la margin-bottom de 0px (à l'état développé) à -2000px à 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 à elle seule pose deux problèmes:

  • Si vous avez réellement une case de plus de 2 000 pixels, alors une 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 case 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é, elle commence 150 ms en retard).

Ce deuxième problème est résolu lorsque la deuxième transition entre en jeu, et cette transition cible conceptuellement la hauteur minimale de l'encapsuleur ("conceptuellement", car nous n'utilisons pas réellement la propriété min-height pour cela; nous y reviendrons plus tard).

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

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 minutage différentes pour les deux transitions, et celles avec qui je me suis retrouvé avaient l'impression qu'elles fonctionnaient le mieux dans 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 masqué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 une max-height: 0 dans le cas 0s 0.3s , avec une transition 0s 0.3s . Cela signifie que ce n'est pas vraiment une transition, mais que la max-height est appliquée avec un délai; cela ne s'applique qu'une fois la transition terminée. Pour que cela fonctionne correctement, nous devons également choisir une 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 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 une height qui passe de 50px à zéro. Cela a le même effet qu'une 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 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. Etant donné que min-height est stronger que max-height , cela ne fonctionnerait pas si nous utilisions la min-height du conteneur au lieu de la min-height du pseudo-élément. Tout comme la max-height du paragraphe précédent, cette max-height é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é sous Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (sauf pour un problème de mise en page de flexbox avec ma démo que je n'ai 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 ne subirez pas de transitions CSS, ce qui en fait un exercice plutôt inutile.


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

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>


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/ (repoussé jjFiddle de stephband ci-dessus)


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

Voir la démo de JSFiddle fournie par Chris Jordan dans une autre answer 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>


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


Vous pouvez, avec un peu de jiggery-pokery non sémantique. Mon approche habituelle consiste à animer la hauteur d’une DIV externe qui n’a qu’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 de .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 en hauteur lorsque l'une des deux tailles ( auto ) est auto .


Solution Flexbox

Avantages:

  • simple
  • 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.

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


Aucune valeur codée en dur.

Pas de JavaScript.

Aucune approximation.

L'astuce consiste à utiliser un élément caché et dupliqué divpour que le navigateur comprenne ce que signifie 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>


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%; /**/
}

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: survol et désactivé, vous pouvez alors contrôler un peu plus la vitesse folle.

Donc, le li: survol est lorsque la souris entre dans l'état et ensuite la transition sur la propriété non survolée sera le 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/


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 le 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 travail autour ... )

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


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 la borne 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/


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

Je ne me suis pas contenté du problème posé par la simple utilisation des max-heighttransitions CSS3, car, comme de nombreux commentateurs l'ont noté, vous devez définir votre max-heightvaleur très près de la hauteur réelle ou vous obtiendrez un retard. Voir ce 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 qui transforme la transform: translateYvaleur CSS.

Cela signifie à la fois max-heightet translateYest utilisé: max-heightpermet à l’élément d’enfoncer les éléments en dessous, tout en translateYdonnant l’effet "instantané" souhaité. Le problème avec max-heightexiste toujours, mais son effet est atténué. Cela signifie que vous pouvez définir une hauteur beaucoup plus grande pour votre max-heightvaleur et vous en soucier moins.

L'avantage général est que lors de la transition (l'effondrement), l'utilisateur voit translateYimmédiatement l' animation, ce qui importe peu de temps max-height.

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>


Ce n'est pas exactement une "solution" au problème, mais plutôt une solution de contournement. Cela fonctionne seulement 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 du JavaScript dans le code, mais son seul but est d'ajouter / 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.

Il a été peu fait mention de la Element.scrollHeightpropriété 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 height: 0élément (effectivement totalement réduit), sa hauteur "normale" ou "complète" est toujours facilement disponible grâce à sa scrollHeightvaleur (invariablement une longueur de pixel ).

Pour un tel élément, en supposant que la transition soit déjà configurée comme par exemple (en utilisant la ulquestion 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 (dans ce cas, la ulvariable fait référence à la liste):

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

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

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

Mon cas particulier de l' utilisation tournait animant autour des listes de longueurs inconnues et souvent considérables, donc je n'étais pas débourbage à l' aise sur un arbitraire « assez grand » heightou max-heightspécification et risque de contenu ou le contenu de coupure que vous avez soudainement besoin pour faire défiler (si overflow: auto, par exemple) . De plus, l’assouplissement et la synchronisation sont rompus avec les max-heightsolutions basées sur les bases, car la hauteur utilisée peut atteindre sa valeur maximale beaucoup plus tôt que ce qu’il faudrait pour max-heightatteindre 9999px. Et à mesure que la résolution de l'écran augmente, les longueurs de pixels me 9999pxlaissent un mauvais goût dans la bouche. 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 widthet height(qui reçoivent actuellement un traitement spécial).


J'ai pu faire ça. J'ai un .child& un .parentdiv. La div de l'enfant s'inscrit parfaitement dans la largeur / hauteur du parent avec le absolutepositionnement. J'anime ensuite la translatepropriété pour faire Ybaisser sa valeur 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 spécifiez un heightsur .parent, dans px, %ou laisser comme auto. Cette div masque alors la .childdiv lorsqu'elle glisse vers le bas.


La réponse acceptée fonctionne dans la plupart des cas, mais ne fonctionne pas bien lorsque la divhauteur varie considérablement. La vitesse de l'animation ne dépend pas de la hauteur réelle du contenu et peut sembler instable.

Vous pouvez toujours effectuer l'animation réelle avec CSS, mais vous devez utiliser JavaScript pour calculer la hauteur des éléments, au lieu d'essayer de l'utiliser auto. JQuery n'est pas requis, mais vous devrez peut-être modifier cela un peu si vous souhaitez 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>


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 (sinon, il y a des retards et des problèmes de synchronisation indésirables). D'un autre côté, si la valeur codée en dur n'est pas accidentellement supérieure à la hauteur réelle, l'élément ne s'ouvre 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. Donc, cette solution pourrait également être utilisée pour le contenu inconnu, par exemple à partir d'une base de données, où vous savez simplement que le contenu n'est généralement pas plus grand que x pixels, mais il y a 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 sur la marge inférieure de -150px à 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, voir mon article de blog http://www.taccgl.org/blog/css_transition_display.html#combined_height


Utilisez-le max-heightavec une transition et un assouplissement 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/


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 défini dur 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 suffit ensuite d’ajouter ou de supprimer un seul attribut booléen 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 sur votre page - Tout ça fonctionne Just Works ™

/ * Code pour la hauteur: auto; en 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);






css-transitions