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

2136

J'essaie de faire une <ul>diapositive en utilisant des transitions CSS.

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

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

Hailwood
la source
Je crois que la height:auto/max-heightsolution ne fonctionnera que si vous agrandissez la zone est supérieure à celle que heightvous souhaitez restreindre. Si vous avez un max-heightde 300px, mais une liste déroulante de zone de liste déroulante, qui peut retourner 50px, puis max-heightne vous aidera pas, 50pxest variable en fonction du nombre d'éléments, vous pouvez arriver à une situation impossible où je ne peux pas le réparer parce que le heightn'est pas fixe, height:autoétait la solution, mais je ne peux pas utiliser de transitions avec cela.
Christopher Thomas
51
OP essaie une solution css, pas js, sinon ils pourraient simplement utiliser le débordement et animer
Toni Leigh
5
@VIDesignz: Mais -100% margin-top du div interne reçoit la largeur du div wrapper, pas la hauteur. Cette solution a donc le même genre de problème, que la solution max-height. De plus, lorsque la largeur est inférieure à la hauteur du contenu, tout le contenu n'est pas masqué par -100% margin-top. C'est donc une mauvaise solution.
GregTom
1
Voir github.com/w3c/csswg-drafts/issues/626 pour une discussion sur les spécifications et la mise en œuvre d'une solution appropriée
Ben Creasy

Réponses:

2746

Utilisez max-heightdans la transition et non height. Et définissez une valeur sur max-heightquelque chose de plus grand que votre boîte ne pourra jamais obtenir.

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

jake
la source
324
cela fonctionne très bien! sauf qu'il y a un retard au démarrage, car il démarre pour une hauteur maximale qui est initialement très élevée..hmm, je pense que c'est un peu ennuyeux
vsync
175
+1 Excellente solution! La vitesse de la transition est calculée est calculée comme le temps que vous spécifiez pour passer à la valeur de hauteur maximale ... mais comme la hauteur sera inférieure à la hauteur maximale, la transition vers la hauteur réelle se produira plus rapidement (souvent de manière significative) que la heure spécifiée.
kingjeffrey
50
Notez que cela peut provoquer une transition moche se terminant lorsque vous devez utiliser des valeurs beaucoup plus grandes que la valeur calculée réelle. J'ai remarqué cela en essayant de faire passer un div de 0 à la hauteur du contenu qui varie considérablement en raison des différentes tailles d'écran (2 lignes sur mon moniteur 2560x1440 vs> 10 lignes sur un smartphone). Pour cela, j'ai fini par aller avec js.
jpeltoniemi
44
Solution très moche car elle crée un retard dans un sens mais pas dans l'autre.
Tim
84
Ceci est une solution assez paresseuse. Je suppose que OP veut utiliser height: auto car la hauteur agrandie du conteneur est quelque peu imprévisible. Cette solution entraînera un délai avant que l'animation ne devienne visible. De plus, la durée visible de l'animation sera imprévisible. Vous obtiendrez des résultats beaucoup plus prévisibles (et probablement plus fluides) en calculant la hauteur combinée de chacun des nœuds enfants des conteneurs, puis en accélérant jusqu'à une valeur de hauteur exacte.
Shawn Whinnery
314

Vous devriez utiliser scaleY à la place.

ul {
  background-color: #eee;
  transform: scaleY(0);    
  transform-origin: top;
  transition: transform 0.26s ease;
}
p:hover ~ ul {
  transform: scaleY(1);
}
<p>Hover This</p>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

J'ai créé une version préfixée par le vendeur du code ci-dessus sur jsfiddle et changé votre jsfiddle pour utiliser scaleY au lieu de height.

dotnetCarpenter
la source
6
Je vote pour cette réponse car elle fonctionne bien dans les navigateurs compatibles avec la transition css3, mais il convient de noter que cela ne fonctionnera pas du tout dans IE <9 et ne sera pas animé (il sautera simplement) dans IE <10
Hailwood
215
Cette méthode n'atteint que partiellement l'effet souhaité mais ne supprime pas réellement l'espace. La boîte transformée agit comme un élément relativement positionné - l'espace est occupé peu importe comment il est mis à l'échelle. Découvrez ce jsFiddle qui prend votre premier et ajoute juste du faux texte en bas. Notez comment le texte en dessous ne se déplace pas vers le haut lorsque la hauteur de la boîte est réduite à zéro.
animuson
9
Maintenant c'est le cas: jsfiddle.net/gNDX3/1 Fondamentalement, vous devez styliser vos éléments en fonction de vos besoins. Il n'y a pas de solution miracle ou de widget comme comportement en CSS / HTML.
dotnetCarpenter
70
Alors que j'applaudis quelqu'un qui essaie différentes approches, l'effet et les complications du monde réel que cette solution apporte sont bien pires que le hack déjà terrible de la hauteur maximale. Veuillez ne pas utiliser.
mystrdat
2
@SquareCat très bien très bien. En voici un qui ressemble plus à un tiroir: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter
202

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

robertc
la source
12
Il existe généralement plusieurs façons de résoudre un problème, et elles ne sont pas toutes appropriées ou possibles. Je ne sais pas pourquoi @JedidiahHurt et Ryan Ore essaient de limiter les réponses possibles sur un site de questions / réponses. Surtout que cette réponse était là en premier. Doublement puisque la réponse acceptée est une solution de contournement.
Hooray Im Helping
5
La réponse de @jake n'est ni élégante ni correcte. La réponse liée ici est bien meilleure. Il fonctionne correctement et gère tous les cas de taille - ni l'un ni l'autre ne peut être dit de la réponse acceptée.
GraphicsMuncher
5
Yup: ça le faitthis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy
4
Est-il prévu que cela soit résolu d'une manière ou d'une autre? Je veux dire, c'est assez naturel de s'attendre à pouvoir passer de la hauteur 0 à auto...
Augustin Riedinger
6
@Meliodas "non / vous ne pouvez pas" est une réponse valide et acceptable aux questions sur Stack Overflow.
TylerH
122

La solution que je l' ai toujours utilisé était d'abord hors de fondu, puis rétrécir les font-size, paddinget les marginvaleurs. Il ne ressemble pas à une lingette, mais il fonctionne sans statique heightoumax-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>

Steven Vachon
la source
A parfaitement fonctionné pour moi. Pas tout à fait comme le jQuery slideUp / Down. Vous ne pouvez voir une différence qu'avec des processeurs faibles, comme les vieux ordinateurs ou les mobiles, et en ouvrir et fermer plusieurs en même temps.
Cruz Nunez
3
Si vous avez des boutons ou tout autre élément non textuel, vous vous retrouverez avec des espaces blancs indésirables sans survoler.
Dennis W
3
Vous pouvez l'affiner davantage en sélectionnant tous les éléments enfants avec *et en appliquant d'autres modifications. Les boutons auraient dû être affectés par mon code ci-dessus, s'ils étaient correctement stylisés avec em.
Steven Vachon
1
Dans mon cas, j'ai également dû ajouter line-height: 0;etborder-width: 0;
Andrey Shatilov
1
Vous ne devriez pas avoir besoin d'ajuster line-heightsi vous évitez correctement les unités sur la valeur: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon
93

Je sais que c'est la trentaine de réponse à cette question, mais je pense que ça vaut le coup, alors voilà. Il s'agit d'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 (expansion et réduction), si vous spécifiez une durée de transition de 300 ms dans votre CSS, la transition prend 300 ms, point.
  • C'est la transition de la hauteur réelle (contrairement transform: scaleY(0)), donc cela fait la bonne chose s'il y a du contenu après l'élément pliable.
  • Bien que (comme dans d'autres solutions) il existe des nombres magiques (comme "choisir une longueur plus élevée que votre boîte ne le sera jamais"), ce n'est pas fatal si votre hypothèse finit par être fausse. La transition peut ne pas sembler incroyable dans ce cas, mais avant et après la transition, ce n'est pas un problème: dans l'état expand ( height: auto), tout le contenu a toujours la bonne hauteur (contrairement par exemple si vous choisissez unmax-height qui se révèle être trop bas). Et à l'état replié, la hauteur est nulle comme il se doit.

Démo

Voici une démo avec trois éléments pliables, tous de hauteurs différentes, qui utilisent tous le même CSS. Vous voudrez peut-être cliquer sur "pleine page" après avoir cliqué sur "exécuter l'extrait". Notez que le JavaScript ne fait que basculer la collapsedclasse CSS, aucune mesure n'est impliquée. (Vous pouvez faire cette démonstration exacte sans aucun 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 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 transitions impliquées pour que cela se produise. L'un d'eux transite margin-bottomde 0px (à l'état développé) à -2000pxl'état réduit (similaire à cette réponse ). Le 2000 ici est le premier chiffre magique, il est basé sur l'hypothèse que votre boîte ne sera pas plus élevée que cela (2000 pixels semble être un choix raisonnable).

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

  • Si vous avez une boîte supérieure à 2000 pixels, alors margin-bottom: -2000pxtout ne sera pas caché - il y aura des choses visibles même dans le cas replié. Il s'agit d'une correction mineure que nous ferons plus tard.
  • Si la zone réelle est, disons, haute de 1000 pixels et que votre transition est longue de 300 ms, alors la transition visible est déjà terminée après environ 150 ms (ou, dans le sens opposé, commence 150 ms en retard).

La correction de ce deuxième problème est là où la deuxième transition entre en jeu, et cette transition cible conceptuellement la hauteur minimale de l'encapsuleur ("conceptuellement" parce que nous n'utilisons pas réellement la min-heightpropriété pour cela; plus à ce sujet plus tard).

Voici une animation qui montre comment la combinaison de la transition de la marge inférieure avec la transition de la hauteur minimale, toutes deux de durée égale, nous donne une transition combinée de la pleine hauteur à la hauteur zéro qui a la même durée.

animation comme décrit ci-dessus

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 de la fermeture, la transition ne se termine pas tôt et dans le cas en expansion, la transition ne commence pas tard. La barre de droite montre comment la combinaison des deux fait passer la boîte de la pleine hauteur à la hauteur zéro dans le bon laps de temps.

Pour ma démo, j'ai choisi 50px comme valeur de hauteur minimale supérieure. Il s'agit du 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 rendre un élément pliable qui n'a même pas 50 pixels de haut en premier lieu.

Comme vous pouvez le voir 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 y a 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 la transition entière 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 synchronisation différentes pour les deux transitions, et celles avec lesquelles je me suis retrouvé me semblaient 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 boîtes de plus de 2000 pixels de hauteur ne sont pas complètement masquées à l'état replié,
  2. et le problème inverse, où dans le cas non caché, les boîtes de moins de 50 pixels de hauteur sont trop hautes même lorsque la transition n'est pas en cours, car la hauteur minimale les maintient à 50 pixels.

Nous résolvons le premier problème en donnant à l'élément conteneur un max-height: 0dans le cas réduit, avec une 0s 0.3stransition. Cela signifie que ce n'est pas vraiment une transition, mais que max-heightc'est appliqué avec un retard; il ne s'applique qu'une fois la transition terminée. Pour que cela fonctionne correctement, nous devons également choisir une valeur numérique max-heightpour l'état opposé, non effondré. Mais contrairement au cas 2000px, où le choix d'un nombre trop important affecte la qualité de la transition, dans ce cas, cela n'a vraiment pas d'importance. Nous pouvons donc simplement choisir un nombre si élevé que nous savons qu'aucune hauteur ne pourra jamais s'en approcher. J'ai choisi un million de pixels. Si vous pensez que vous devrez peut-être prendre en charge 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-heightpour la transition de hauteur minimale. Au lieu de cela, il y a un ::afterpseudo-élément dans le conteneur avec un heightqui passe de 50px à zéro. Cela a le même effet qu'un min-height: il ne laissera pas le conteneur rétrécir en dessous de la hauteur actuelle du pseudo-élément. Mais parce que nous utilisons height, non min-height, nous pouvons maintenant utiliser max-height(une fois de plus appliqué avec un retard) pour définir la hauteur réelle du pseudo-élément à zéro une fois la transition terminée, garantissant qu'au moins en dehors de la transition, même les petits éléments ont le hauteur correcte. Parce que min-heightest plus fort que max-height, cela ne fonctionnerait pas si nous utilisions le conteneur au min-heightlieu du pseudo-élémentheight. Tout commemax-heightdans le paragraphe précédent, cela max-heightnécessite également 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, IE11 (à l'exception d'un problème de mise en page flexbox avec ma démo que je n'ai pas pris la peine de déboguer) et Safari (Mac, iOS ). En parlant de flexbox, il devrait être possible de faire ce travail sans utiliser de flexbox; en fait, je pense que vous pourriez 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.

balpha
la source
36
Je souhaite que vous puissiez raccourcir (simplifier) ​​votre solution, ou l'exemple de code au moins - il semble que 90% de l'exemple de code ne soit pas pertinent pour votre réponse.
Gil Epshtain
Existe-t-il un moyen de faire fonctionner ces transitions si le collapsible-wrappera une position absolue. Le overflow: hiddensur le parent est requis pour la transition mais interfère avec les éléments absolument positionnés.
pmkro
1
@pmkro Ouais, j'ai aussi eu ce problème. Je l'ai résolu en utilisant un clip-path: polygon(...)au lieu de overflow: hidden, car contrairement au dernier, vous pouvez faire la transition du premier. Pour remplacer les anciens navigateurs, j'ai utilisé une transformation de mise à l'échelle supplémentaire. Voir le commentaire en haut de github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha
2
Agréable. Ça marche vraiment bien. Devrait être la réponse acceptée
Henry Ing-Simmons
84

Vous pouvez, avec un peu de jiggery-pokery non sémantique. Mon approche habituelle consiste à animer la hauteur d'un DIV externe qui a un seul enfant qui est un DIV sans style utilisé 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 aimerait juste pouvoir se passer de .measuringWrapperet régler la hauteur du 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 la hauteur (la hauteur de début ou de fin) est auto.

jhurshman
la source
10
Comme cela repose sur javascript, vous pouvez également ajouter facilement le mesureWrapper en utilisant javascript aussi!
Quickredfox
18
Vous pouvez le faire sans emballage. Just: function growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
user1742529
8
Vérifiez ceci ... parlez d'une réponse simple jsfiddle.net/n5XfG/2596 ... Votre réponse est incroyablement complexe sans raison valable. Pas besoin max-height, pas besoin auto, et surtout pas besoin de Javascript !! Juste deux déclarations simples
VIDesignz
12
@VIDesignz Vous animez sur la marge: 100%. C'est 100% de la largeur de l'élément, pas sa hauteur! Cela signifie que si vous avez un élément avec une hauteur supérieure à sa largeur, cela ne le masquera pas. De plus, la vitesse d'animation réelle est totalement différente de celle qui est définie.
Timo Türschmann
3
J'étais excité par la réponse de VIDesignz jusqu'à ce que j'en lise plus sur le commentaire de Timo. Oui, margin-top(et margin-bottom) sont relatifs à la largeur lorsqu'ils sont définis en pourcentages, oui, c'est stupide, mais cela explique également pourquoi les temporisations d'animation d'ouverture et de fermeture sont complètement différentes - c'est la même chose que vous voyez dans la réponse la mieux notée, en spécifiant un dur -hauteur max codée. Pourquoi est-ce si difficile à faire correctement?
Coderer
52

Une solution visuelle pour animer la hauteur à l'aide de transitions CSS3 consiste à animer le remplissage à la place.

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

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/ (riffed au-dessus de stephband's jsFiddle)

Catharsis
la source
3
Je pense que c'est la meilleure réponse. Cette technique peut également s'étendre au réglage du rembourrage des enfants. Dans mon cas, j'ai pu le combiner avec des transitions de hauteur explicites sur certains des contenus révélés, et l'effet total est beaucoup plus réussi que le contour de la hauteur maximale, ce qui est bien pour la révélation mais introduit un moment gênant de retard sur la peau. Je préfère ne pas animer du tout que d'introduire un retard insignifiant qui fait que mon application ne répond plus. Je suis surpris que tant de gens semblent penser que c'est acceptable.
Point
17
Sauf qu'ici, vous n'animez pas du tout la hauteur. Vous animez le remplissage ... il peut très bien disparaître car il peut animer de l'état actuel jusqu'à 0, mais si vous regardez attentivement quand il se développe, il s'ouvre avec le texte et le remplissage ne s'anime que .. parce qu'il ne fonctionne pas sais pas comment animer de 0 à auto ... il a besoin d'une plage numérique ... c'est comme ça que fonctionne l'interpolation.
Rob R
Il s'agit d'une bonne solution de contournement qui n'est pas hacky. Ce n'est pas la meilleure réponse à cette question, mais c'est une alternative qui a fonctionné pour moi. Parfois, vous avez juste besoin de l'effet d'une animation en hauteur, pas de l'animation complète :)
Ivan Durst
Cette solution échoue également lors de l'utilisation d'un délai de transition.
Michel Joanisse
Je conviens également que cette solution est une solution de contournement propre qui fournit des transitions assez fluides si votre animation est assez rapide (dans mon cas pour un accordéon avec des transitions de 0,1 s, c'est parfait!). Il ne comprendra pas beaucoup d'applications, mais les autres ne répondront pas non plus à cette question!
Remi D
46

Ma solution consiste à faire passer la hauteur maximale à la hauteur exacte du contenu pour une animation fluide et agréable, puis à utiliser un rappel transitionEnd pour définir la hauteur maximale à 9999 pixels 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>

Adam
la source
14
@ Derija93 qui utilise de vieilles animations de timeout javascript. Le défi consiste à utiliser des transitions CSS3 à la place.
Adam
3
Hé bien oui. Mais pourquoi utiliseriez-vous des «transitions CSS3 à la place» si vous, dans votre exemple, utilisez déjà jQuery, une bibliothèque, qui fournit quand même beaucoup de code «écrivez moins, faites plus»? Je voulais souligner que votre version pourrait être bien meilleure, même si elle est plus compliquée, si elle n'utilisait PAS jQuery pour pouvoir fonctionner sur pratiquement n'importe quel site Web. Je suppose que je l'ai énoncé très mal. Désolé pour ça. ;)
Kiruse
27
@ Derija93 Peut-être parce que les transitions CSS3 ont (selon toutes les sources que je peux trouver) des performances bien meilleures que les animations jQuery. (En fait, cela compte énormément pour mon cas d'utilisation actuel, qui m'a amené ici.)
Mark Amery
4
@ Derija93 Parce que les animations Javascript s'exécutent lentement par rapport aux transitions CSS3 bro, et avec les animations jQuery, vous devez vous soucier de la boucle d'animation. Animez quelque chose au clic, puis cliquez rapidement et regardez les animations se répéter. Ceci est géré avec CSS3.
AlbertEngelB
2
@ Dropped.on.Caprica C'est un peu vieux maintenant. J'ai déjà dit que j'avais mal compris le concept qu'il essayait de démontrer. Quoi qu'il en soit, pour les animations simples, .stopfait l'affaire pour le problème de boucle d'animation plutôt complexe. Maintenant, lorsque vous animez la hauteur et la couleur mais que vous souhaitez interrompre uniquement l'animation de la hauteur, les choses deviennent un peu plus délicates ... Je comprends votre point.
Kiruse
43

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

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 d'utiliser auto. Aucune jQuery n'est requise, bien que vous deviez peut-être la modifier 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>

Oleg Vaskevich
la source
Cela peut être utile, mais je ne peux pas le dire car je ne sais pas comment l'utiliser sans un meilleur exemple.
Ivan Durst
1
Vous passez simplement n'importe quel élément DOM (par exemple, à partir de document.getElementById('mydiv')ou $('#mydiv').get()), et la fonction bascule entre le masquer / l'afficher. Si vous configurez des transitions CSS, l'élément s'animera automatiquement.
Oleg Vaskevich
J'ai ajouté un extrait. Cela a l'avantage de bien fonctionner pour un contenu de taille dynamique.
Oleg Vaskevich
3
-1, car il ne s'agit pas "d'utiliser CSS". Le point de la question est pour une solution sans JS. Je pense que la "bonne" réponse, ce n'est pas un hack, serait la suivante, cependant ...
dudewad
16
Votez pour l'utilisation de JS - sérieusement? La moitié des réponses ici utilisent JS, donc votre downvote ne semble pas juste ou nécessaire. En outre, cette réponse utilise toujours CSS pour effectuer l'animation réelle, ce qui était le but de l'OP de l'OMI. La seule chose pour laquelle JS est utilisé est de calculer la hauteur réelle, car il est impossible de le faire avec du CSS pur (et le réglage min-heightcomme dans la réponse attendue entraîne des problèmes de vitesse d'animation lorsque la taille de la liste peut varier considérablement).
Oleg Vaskevich
30

Il y avait peu de 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 ou non en raison de la hauteur réduite (par exemple height: 0).

En tant que tel, pour un height: 0élément (effectivement complètement effondré), sa hauteur "normale" ou "pleine" est toujours facilement disponible grâce à sa scrollHeightvaleur (invariablement une longueur de pixel ).

Pour un tel élément, en supposant qu'il a déjà la transition configurée comme par exemple (en utilisant ulselon la question d'origine):

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

Nous pouvons déclencher "l'expansion" animée souhaitée de la hauteur, en utilisant uniquement CSS, avec quelque chose comme ce qui suit (ici en supposant que la ulvariable se réfère à la liste):

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

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

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, donc je n'étais pas à l'aise de m'installer sur une "assez grande" heightou une max-heightspécification arbitraire et de risquer le contenu coupé ou le contenu que vous devez soudainement faire défiler (si overflow: auto, par exemple) . De plus, l'assouplissement et le timing 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 qu'elle ne le faudrait pour l' max-heightatteindre 9999px. Et à mesure que les résolutions d'écran augmentent, les longueurs de pixels comme 9999pxlaissent un mauvais goût dans ma bouche. Cette solution particulière résout le problème d'une manière élégante, à mon avis.

Enfin, nous espérons que les futures révisions des adresses CSS devront faire ce genre de choses encore plus élégamment - revisiter la notion de valeurs "calculées" vs "utilisées" et "résolues", et examiner si les transitions devraient s'appliquer aux données calculées valeurs, y compris les transitions avec widthet height(qui bénéficient actuellement d'un traitement spécial).

amn
la source
6
Vous ne l'avez pas trouvé dans les autres réponses ici, car l'interaction avec le DOM à l'aide de la Element.scrollHeightpropriété nécessite JavaScript et, comme l'indique clairement la question, ils veulent le faire sans JavaScript.
TylerH
3
Il existe de nombreuses autres parties de CSS autres que ces deux pseudo-classes qui peuvent résoudre ce problème, comme le montrent les réponses les plus appréciées à la page 1. Définir un style en JavaScript n'est pas du "pur CSS" car il nécessite que JS soit défini en premier lieu. Souvent, lorsqu'une personne demande une solution CSS uniquement, c'est parce qu'elle n'est pas en mesure d'injecter du JavaScript ou qu'elle n'y est pas autorisée par son projet ou son rôle actuel.
TylerH
29

Utilisation max-height avec un assouplissement 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 l'exemple: http://jsfiddle.net/0hnjehjc/1/

malihu
la source
12
Le problème ici est que, pour vous assurer d'avoir suffisamment d'espace dans un environnement dynamique, vous devez utiliser des hauteurs ridicules comme 999999px. Cela, en revanche, décale votre animation, car les images sont calculées à partir de cette valeur. Vous pourriez donc vous retrouver avec un "retard" d'animation dans un sens et une fin très rapide dans l'autre sens (corr: timing-functions)
Julian F. Weinert
29

Pas de valeurs codées en dur.

Pas de JavaScript.

Aucune approximation.

L'astuce consiste à utiliser un caché et dupliqué div pour 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>

Vivek Maharajh
la source
Bien joué - c'est une solution valable à un problème qui ne devrait vraiment pas exister. L'animation de max-height est bas, sauf si la transition est définie sur "linéaire", c'est aussi (évidemment) très mauvais pour animer du contenu CMS où la hauteur est variable.
M_Willett
c'est intelligent, mais je ne serais pas à l'aise de dupliquer des éléments et du contenu DOM pour cela.
Andre Figueiredo
Vous pouvez le faire sans conteneur et plus performant en animant transform: scaleY(1), car cette transition ne supprime pas l'espace.
Fabian von Ellerts
21

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

Je n'étais pas satisfait du problème qui résulte de 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 cela (tout en n'utilisant pas de JavaScript), j'ai ajouté un autre élément HTML qui transite la transform: translateYvaleur CSS.

Ce moyen à la fois max-heightet translateYsont utilisés: max-heightpermet à l'élément de pousser vers le bas des éléments ci - dessous, tout en translateYdonne l'effet « instantané » nous voulons. 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 de retour (l'effondrement), l'utilisateur voit translateYimmédiatement l' animation, donc peu importe le temps que cela max-heightprend.

Solution en tant que 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>

Andrew Messier
la source
Belle comparaison @davidtaubmann, votre stylo illustre très bien les différences! Je suis allé avec translateYplus scaleparce que je ne l' ai pas comme la façon dont scalea donné cet effet de compression.
Andrew Messier
Merci @ andrew-messier, j'ai supprimé le commentaire car il y a une erreur, translateY n'exécute pas dans codepen.io/davidtaubmann/pen/zKZvGP (car il n'y a pas d'enfant pour faire le déplacement, a besoin d'une correction) ... mais dans ce celui que nous pouvons parfaitement voir la comparaison de SEULEMENT MAX-HEIGHT et le mélanger avec l'échelle (comme indiqué dans d'autres réponses): codepen.io/davidtaubmann/pen/jrdPER . Tout cela m'a beaucoup aidé avec une méthodologie Target que j'utilise
DavidTaubmann
18

Ok, donc je pense que j'ai trouvé une réponse super simple ... non max-height, utilise le relativepositionnement, travaille sur les liéléments, et c'est du pur CSS. Je n'ai testé rien d'autre que Firefox, bien qu'à en juger par le CSS, il 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>
VIDesignz
la source
13
Cela fonctionnera jusqu'à ce que la hauteur de l'élément dépasse sa largeur. C'est la base de la façon dont les marges sont calculées à l'aide de pourcentages: elles sont calculées en fonction de la largeur de l'élément. Donc, si vous avez un élément de 1000 px de large, alors un élément à 1100 px sera trop grand pour que cette solution fonctionne, ce qui signifie que vous devrez augmenter cette marge supérieure négative. Fondamentalement, c'est exactement le même problème que l'utilisation de la hauteur ou de la hauteur maximale.
dudewad
15

EDIT: faites défiler vers le bas 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 n'utilisera que du CSS pour menu déroulant! J'ai utilisé transform: translateY (y) pour transformer la liste en vue ...
Vous pouvez en voir plus dans le test
http://jsfiddle.net/BVEpc/4/
J'ai placé div derrière chaque li parce que mon la liste déroulante vient de haut et pour leur montrer correctement 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 planer 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 dépasser le contenu de votre corps, c'est pourquoi j'ai fait cela pour ul:

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

et survoler:

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

la deuxième fois après la transition est un retard et il sera caché après la fermeture animée de ma liste déroulante ...
J'espère que quelqu'un bénéficiera de celle-ci plus tard.

EDIT: Je ne peux pas croire que ppl utilise réellement ce prototype! ce menu déroulant est uniquement pour un sous-menu et c'est tout !! J'ai mis à jour un meilleur qui peut avoir deux sous-menus pour la direction ltr et rtl avec le support IE 8.
Fiddle for LTR
Fiddle for RTL
j'espère que quelqu'un trouvera cela utile à l'avenir.

Sijav
la source
11

Vous pouvez passer de la hauteur: 0 à la hauteur: auto à condition de fournir également la hauteur minimale et la hauteur maximale.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
Stuart Badminton
la source
@Stuart Badminton pouvez-vous fournir un exemple de travail? J'ai mis cela ensemble mais cela ne semble fonctionner nulle part :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin
5
Le problème vient de votre règle de transition. pour le faire fonctionner, vous devez également appliquer la transition à la hauteur minimale et maximale. transition: hauteur .5s linéaire, min-hauteur .5s linéaire, max-hauteur .5s linéaire
Stuart Badminton
2
voici ce que j'ai plus tard, comme vous l'avez dit, pour animer max-height: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin
11
Bien sûr, cela pose un problème. Le temps qu'il faut pour animer max-height n'est pas le temps qu'il faut pour passer à la hauteur automatique de la boîte: il arrive à hauteur: auto d'abord, avant que max-height ait fini d'animer. De même, lors de la transition vers 0, la transition ne commence pas immédiatement, car max-height commence à une hauteur supérieure à height: auto. Transition vers max-height: 1000px le dit clairement: jsfiddle.net/n5XfG/11
stephband
Vous avez raison, lors de tests supplémentaires, j'ai remarqué cela. Je ne sais pas si une pleine hauteur: 0 à height: auto sera jamais implémentée, mais l'utilisation d'une hauteur maximale dans certaines situations résout une situation autrement impossible en utilisant CSS seul.
Stuart Badminton
10

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 consiste à toujours avoir flex-base: auto sur l'élément avec le contenu, et à la place la transition flex-grow et flex-shrink.

Edit: Amélioration de JS Fiddle inspiré par 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

Lee Comstock
la source
7

En développant la réponse de @ jake, la transition ira jusqu'à la valeur de hauteur maximale, provoquant une animation extrêmement rapide - si vous définissez les transitions pour les deux: survolez et éteignez, vous pouvez alors contrôler un peu plus la vitesse folle.

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

Jamie
la source
1
Je ne sais pas pourquoi cela a obtenu un -1. En fait, je pense que c'est une meilleure tentative que certaines personnes ajoutant des charges de javascript. Ce n'est pas une solution parfaite, mais avec quelques ajustements, cela fait du bon travail, j'ai trouvé que cela fonctionnait pour la valeur de retour. transition: tous les 500 ms cube-bezier (0,000, 1,225, 0,085, 0,385); Ceci était basé sur une transition de 2s à l'ouverture puis sur le code ci-dessus pour revenir à l'état initial. Merci ... Cela m'a aidé mieux que tout ce qui précède. +1 J'ai utilisé ce site pour créer le bezier matthewlein.com/ceaser
gcoulby
OK, il a fallu un peu plus de peaufinage. transition: tous les 500 ms cube-bezier (0,000, 1,390, 0,000, 0,635); Je dois préciser que ma transition passe de 5% à 100% (mais en réalité la valeur finale est d'environ 28%) donc cela dépend du projet.
gcoulby
Absolument, ce n'est pas idéal, mais c'est une solution rapide si vous savez ce que sera la hauteur moyenne.
jamie
7

Je pense avoir trouvé une solution vraiment solide

D'ACCORD! Je sais que ce problème est aussi ancien qu'Internet mais je pense avoir une solution que j'ai transformée en un plugin appelé mutant-transition . Ma solution définit les style=""attributs des éléments suivis chaque fois qu'il y a un changement dans le DOM. le résultat final est que vous pouvez utiliser un bon vieux CSS pour vos transitions et ne pas utiliser de correctifs hacky ou de javascript spécial. La seule chose que vous avez à faire est de définir ce que vous souhaitez suivre sur l'élément en question à l'aide 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 changements dans le DOM. Pour cette raison, vous n'avez pas vraiment besoin de configurer quoi que ce soit ou d'utiliser javascript pour animer manuellement les choses. Les modifications sont suivies automatiquement. Cependant, car il utilise MutationObserver, cela ne fera que la transition dans IE11 +.

Fiddles!

JonTroncoso
la source
12
Habituellement, lorsque quelqu'un demande comment faire quelque chose "sans JavaScript", cela signifie que la solution doit fonctionner sur les navigateurs pour lesquels JavaScript est désactivé. Cela ne signifie pas «sans écrire mes propres scripts». Donc, dire que votre plugin peut être utilisé sans utiliser JavaScript est au mieux trompeur - étant donné qu'il s'agit d'un plugin JavaScript.
BoltClock
Je dois préciser, quand je dis que vous n'avez pas besoin d'utiliser de javascript spécial, je veux dire que vous n'avez pas à écrire de javascript. il suffit d'inclure la bibliothèque JS et de spécifier les attributs que vous souhaitez regarder dans le HTML. Vous n'avez pas besoin d'utiliser un CSS à hauteur fixe ou de trouver quoi que ce soit. il suffit de coiffer et de partir.
JonTroncoso
"(...) vous n'avez pas vraiment besoin de configurer quoi que ce soit ou d'utiliser javascript pour animer manuellement les choses (...)" donc vous utilisez JavaScript pour le plaisir
Roko C. Buljan
6

Voici un moyen de passer de n'importe quelle hauteur de départ, y compris 0, à auto (pleine taille et flexible) sans nécessiter de code défini sur une base par nœud ou de code utilisateur pour initialiser: https://github.com/csuwildcat / transition-auto . C'est fondamentalement le Saint Graal pour ce que vous voulez, je crois -> http://codepen.io/csuwldcat/pen/kwsdF . Insérez simplement le fichier JS suivant dans votre page, et tout ce que vous devez faire après cela est d'ajouter / 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, c'est toute la sauce:

Déposez ce fichier JS dans votre page - tout cela fonctionne correctement ™

/ * 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);
csuwldcat
la source
9
Bien que ce soit une solution beaucoup plus propre et flexible que les autres qui ont été suggérées, je pense personnellement que cela ne devrait pas être quelque chose que JS devrait toucher du tout. Ne dis pas que tu as tort, c'est juste un soupir.
mystrdat
@mystrdat pour être clair cependant, JS n'est utilisé que pour surveiller certaines conditions et ajouter quelques attributs / valeurs CSS temporaires en fonction des événements / conditions qu'il surveille. La transition se fait toujours en CSS. Bien que je sois d'accord, il est triste que ce niveau de configuration et de prise en main soit nécessaire pour vraiment satisfaire un cas d'utilisation apparemment simple! : /
csuwldcat
1
@KarolisRamanauskas non, ce n'est pas un problème - IE prend en charge les images clés CSS et requestAnimationFrame. J'utilisais clipcomme déclencheur de propriété factice, et pour une raison quelconque, IE a cessé d'autoriser l'animation du clip. Je suis passé à l'opacité, et tout fonctionne à nouveau: codepen.io/csuwldcat/pen/FpzGa . J'ai mis à jour le code dans la réponse pour qu'il corresponde.
csuwldcat
1
@csuwldcat Très belle solution, j'ai remarqué que le contenu apparaît et disparaît pour une petite fraction, pourquoi cela se produit-il? Est-il évitable?
Beto Aveiga
12
Je veux voter positivement, mais au lieu de répondre à la question de savoir comment le faire fonctionner, vous avez partagé un plugin que vous avez écrit pour le faire fonctionner. Nous, les curieux, devons faire de l'ingénierie inverse de votre plugin, ce qui n'est pas très amusant. Je souhaite que vous mettiez à jour votre réponse pour contenir plus d'explications sur ce que fait votre plugin et pourquoi. Modifiez le code pour être plus explicatif. Par exemple, vous avez une section entière de code qui écrit simplement du CSS statique. Je préfère voir le CSS, que le code qui le génère. Vous pouvez laisser de côté les parties ennuyeuses, comme la répétition pour tous les préfixes du navigateur.
gilly3
6

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

On pourrait déplacer le contenu pliable dans une div intérieure et calculer la hauteur maximale en obtenant la hauteur de la div intérieure (via JQuery ce serait le externeHeight ()).

$('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 requise: http://jsfiddle.net/8ncjjxh8/

pbatey
la source
5

Je me rends compte que ce fil vieillit, mais il occupe un rang élevé dans certaines recherches Google, donc je pense qu'il vaut la peine d'être mis à jour.

Vous obtenez également / définissez simplement la hauteur de l'élément:

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

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

Charley
la source
document.getElementById ('target_box'). getBoundingClientRect (). height devrait faire de son mieux.
modifier
5

J'ai pu faire ça. J'ai un .child& un .parentdiv. Le div enfant s'intègre parfaitement dans la largeur / hauteur du parent avec le absolutepositionnement. J'anime ensuite la translatepropriété pour faire Ybaisser sa valeur100% . Son animation très fluide, sans pépins ni 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 congé comme auto. Ce div masque ensuite le .childdiv lorsqu'il glisse vers le bas.

Josh Ribakoff
la source
L'exemple de travail de ceci sera très apprécié.
Neurotransmetteur
5

J'ai posté une réponse avec du JavaScript et j'ai été rétrogradé, alors j'ai été ennuyé et j'ai essayé à nouveau, et je l'ai craqué avec CSS uniquement!

Cette solution utilise quelques «techniques»:

  • padding-bottom:100%'hack' où les pourcentages sont définis en termes de largeur actuelle de l'élément. Plus d'informations sur cette technique.
  • float rétractable, (nécessitant une division supplémentaire pour appliquer le hack de compensation du flotteur)
  • utilisation non sémantique de https://caniuse.com/#feat=css-writing-mode et quelques transformations pour l'annuler (cela permet d'utiliser le hack de remplissage ci-dessus dans un contexte vertical)

Le résultat est que nous obtenons une transition performante en utilisant uniquement CSS, et une seule fonction de transition pour réaliser la transition en douceur; le Saint-Graal!

Bien sûr, il y a un inconvénient! Je ne peux pas déterminer comment contrôler la largeur à laquelle le contenu est coupé ( overflow:hidden); en raison du hack de rembourrage, la largeur et la hauteur sont intimement liées. Il y a peut-être un moyen, alors j'y reviendrai.

https://jsfiddle.net/EoghanM/n1rp3zb4/28/

body {
  padding: 1em;
}

.trigger {
  font-weight: bold;
}

/* .expander is there for float clearing purposes only */
.expander::after {
  content: '';
  display: table;
  clear: both;
}

.outer {
  float: left; /* purpose: shrink to fit content */
  border: 1px solid green;
  overflow: hidden;
}

.inner {
  transition: padding-bottom 0.3s ease-in-out;  /* or whatever crazy transition function you can come up with! */
  padding-bottom: 0%;  /* percentage padding is defined in terms of width. The width at this level is equal to the height of the content */
  height: 0;

  /* unfortunately, change of writing mode has other bad effects like orientation of cursor */
  writing-mode: vertical-rl;
  cursor: default; /* don't want the vertical-text (sideways I-beam) */
  transform: rotate(-90deg) translateX(-100%);  /* undo writing mode */
  transform-origin: 0 0;
  margin: 0;  /* left/right margins here will add to height */
}

.inner > div { white-space: nowrap; }

.expander:hover .inner,  /* to keep open when expanded */
.trigger:hover+.expander .inner {
  padding-bottom: 100%;
}
<div class="trigger">HoverMe</div>
<div class="expander">
  <div class="outer">
    <div class="inner">
      <div>First Item</div>
      <div>Content</div>
      <div>Content</div>
      <div>Content</div>
      <div>Long Content can't be wider than outer height unfortunately</div>
      <div>Last Item</div>
    </div>
  </div>
</div>
<div>
  after content</div>
</div>

EoghanM
la source
L'autre inconvénient est que vous ne pouvez pas déplacer le curseur hors de la div "hoverme" sur la liste des éléments afin d'interagir avec eux (ostensiblement la raison de la demande OP), il n'est donc utile qu'en tant qu'infobulle plutôt que disons un menu avec des sous-menus imbriqués.
TylerH
C'est purement une conséquence de la façon dont la question a été formulée et n'est pas liée à la technique. Vous pouvez changer le déclencheur pour être un input[type="checkbox"]:checkedau lieu de :hoversi vous le souhaitez (c'est-à-dire que vous ne voulez pas utiliser JavaScript pour ajouter des classes)
EoghanM
Je veux dire, quelle que soit la façon dont la question est formulée, si la mise en œuvre d'une solution est si limitée, elle devrait probablement être mentionnée dans la réponse de cette solution (ou de préférence expliquée ). Vous mentionnez que cette solution est un «Saint-Graal», mais le div en transition ne peut même pas être survolé ... ne me semble pas être une solution ultime.
TylerH
Salut @TylerH, excuses, j'ai raté le fait que le déclencheur de la question d'origine entoure la liste développée, donc la liste devrait rester ouverte une fois survolée. J'ai mis à jour ma réponse pour refléter cela. Comme je l'ai mentionné cependant, la façon dont il est déclenché n'est pas si pertinente, car je présente une nouvelle technique qui n'était pas auparavant envisageable en CSS uniquement (je cherche une solution depuis quelques années).
EoghanM
4

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 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 }
HandiworkNYC.com
la source
cela ne s'affiche pas correctement avec la dernière version de jquery
oliverbachmann
4

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

Le raisonnement est que le délai pour les petits max-heightsest beaucoup moins perceptible (voire pas du tout) par rapport aux grands max-heights, et je peux également définir ma max-heightvaleur par rapport à font-sizela liplutôt qu'à un nombre énorme arbitraire en utilisant emsou rems.

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

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

pleine conscience
la source
3

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 un div qui affiche d'abord du contenu jusqu'à 200px de hauteur et en vol stationnaire, sa taille devient au moins aussi élevée que le contenu entier du div. La Div ne devient pas 3000px mais 3000px est la limite que j'impose. Assurez-vous d'avoir la transition sur le non: hover, sinon vous pourriez obtenir un rendu étrange. De cette façon, le: hover hérite du non: hover.

La transition ne fonctionne pas du format px vers% ou vers auto. Vous devez utiliser la même unité de mesure. Cela fonctionne bien pour moi. L'utilisation de HTML5 le rend parfait ....

N'oubliez pas qu'il y a toujours un travail à faire ...; )

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

work.stella84
la source
3

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 indésirables et des problèmes de synchronisation). D'un autre côté, si la valeur codée en dur n'est pas accidentellement plus grande que la hauteur réelle, l'élément ne s'ouvrira pas complètement.

La solution CSS uniquement suivante nécessite également une taille codée en dur qui doit être supérieure à la plupart des tailles réelles présentes. Cependant, cette solution fonctionne également si la taille réelle est dans certains cas plus grande que la taille codée en dur. Dans ce cas, la transition pourrait sauter un peu, mais elle ne laissera jamais un élément partiellement visible. Cette solution pourrait donc également être utilisée pour du 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 de contenu dans un élément central avec débordement: caché. La marge négative de l'élément de contenu réduit ainsi la hauteur de l'élément central.

Le code suivant utilise une transition sur margin-bottom de -150px à 0px. Cela seul fonctionne très bien tant que l'élément de contenu n'est pas supérieur à 150 px. De plus, il utilise une transition sur max-height pour l'élément central de 0px à 100%. Cela masque finalement l'élément central si l'élément de contenu est supérieur à 150 px. Pour la hauteur maximale, la transition est juste utilisée pour retarder son application d'une seconde lors de la fermeture, pas pour un effet visuel fluide (et donc elle peut 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 de margin bottom doit être négative et aussi proche que possible de la hauteur réelle de l'élément de contenu. Si elle (la valeur d'absoute) est plus grande, il existe des problèmes de retard et de synchronisation similaires à ceux des solutions de hauteur maximale, qui peuvent cependant être limités tant que la taille codée en dur n'est pas beaucoup plus grande que la vraie. 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 entièrement affiché ou entièrement supprimé.

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

Helmut Emmelmann
la source
3

Vous pouvez le faire en créant une animation inverse (repliée) avec clip-path.

#child0 {
    display: none;
}
#parent0:hover #child0 {
    display: block;
    animation: height-animation;
    animation-duration: 200ms;
    animation-timing-function: linear;
    animation-fill-mode: backwards;
    animation-iteration-count: 1;
    animation-delay: 200ms;
}
@keyframes height-animation {
    0% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 0%, 0% 0%);
    }
    100% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 100%, 0% 100%);
    }
}
<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>

Andrii
la source
2

Ce n'est pas exactement une «solution» au problème, mais plutôt une solution de contournement. Il fonctionne uniquement comme écrit avec du texte, mais peut être modifié pour fonctionner avec d'autres éléments selon les besoins, je 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

Essentiellement, vous définissez la taille de la police sur 0 et effectuez la transition au lieu de la hauteur, ou la hauteur maximale, ou scaleY (), etc. à un rythme suffisamment rapide pour que la hauteur se transforme en ce que vous voulez. Transformer la hauteur réelle avec CSS en auto n'est actuellement pas possible, mais la transformation du contenu à l'intérieur l'est, d'où la transition de la taille de la police.

  • Remarque - il y a du javascript dans le codepen, 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 ne me suis pas concentré sur cela, juste sur la transformation de la hauteur.
Mike S.
la source
1
Duplique efficacement cette réponse , mais omet l'effet d'opacité-fondu.
SeldomNeedy
D'accord. Meilleure approche.
Mike S.