Come posso spostare l'altezza: 0; all'altezza: auto; usando CSS?


2136

Sto cercando di fare una <ul>diapositiva verso il basso usando le transizioni CSS.

L' <ul>inizio inizia alle height: 0;. Al passaggio del mouse, l'altezza è impostata su height:auto;. Tuttavia, ciò sta causando la semplice visualizzazione, non la transizione,

Se lo faccio da height: 40px;a height: auto;, allora scivolerà su height: 0;e poi salterà improvvisamente all'altezza corretta.

In quale altro modo posso farlo senza usare 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>


Credo che la height:auto/max-heightsoluzione funzionerà solo se l'area in espansione è maggiore di quella che heightsi desidera limitare. Se hai un max-heightdi 300px, ma un menu a discesa della casella combinata, che può restituire 50px, quindi max-heightnon ti aiuterà, 50pxè variabile a seconda del numero di elementi, puoi arrivare a una situazione impossibile in cui non posso ripararlo perché non lo heightè risolto, height:autoera la soluzione, ma non posso usare le transizioni con questo.
Christopher Thomas,

51
OP sta cercando una soluzione CSS, non js, altrimenti potrebbero semplicemente usare overflow e animare
Toni Leigh

5
@VIDesignz: Ma il margine superiore -100% del div interno riceve la larghezza del div dell'involucro, non l'altezza. Quindi questa soluzione ha lo stesso tipo di problema, che la soluzione di altezza massima. Inoltre, quando la larghezza è inferiore all'altezza del contenuto, non tutto il contenuto viene nascosto di -100% margin-top. Quindi questa è una soluzione sbagliata.
GregTom,

1
Vedi github.com/w3c/csswg-drafts/issues/626 per la discussione sulle specifiche e l'implementazione di una soluzione adeguata
Ben Creasy,

Risposte:


2745

Utilizzare max-heightnella transizione e non height. E imposta un valore su max-heightqualcosa di più grande di quanto possa mai avere la tua scatola.

Guarda la demo di JSFiddle fornita da Chris Jordan in un'altra risposta qui.

#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>


324
funziona alla grande! tranne che c'è un ritardo all'avvio, perché inizia per l'altezza massima che inizialmente è molto alta..hmm, penso che questo sia un po 'fastidioso
vsync

175
+1 Ottima soluzione! La velocità della transizione viene calcolata come il tempo specificato per la transizione al valore di altezza massima ... ma poiché l'altezza sarà inferiore all'altezza massima, la transizione all'altezza effettiva avverrà più velocemente (spesso in modo significativo) rispetto alla tempo specificato.
Kingjeffrey,

50
Si noti che ciò può causare una brutta fine della transizione quando si devono usare valori molto più grandi del valore effettivo calcolato. Ho notato questo mentre cercavo di far crescere un div da un'altezza 0 all'altezza del contenuto che varia notevolmente a causa delle diverse dimensioni dello schermo (2 linee sul mio monitor 2560x1440 vs> 10 linee su uno smartphone). Per questo ho finito per andare con js.
jpeltoniemi,

44
Soluzione molto brutta poiché crea un ritardo in una direzione ma non nell'altra.
Tim

84
Questa è una soluzione piuttosto pigra. Suppongo che OP voglia usare l'altezza: auto perché l'altezza estesa del contenitore è alquanto imprevedibile. Questa soluzione causerà un ritardo prima che l'animazione diventi visibile. Inoltre, la durata visibile dell'animazione sarà imprevedibile. Otterrai risultati molto più prevedibili (e probabilmente più fluidi) calcolando l'altezza combinata di ciascuno dei nodi figlio del contenitore e quindi spostandoti a un valore di altezza esatto.
Shawn Whinnery,

314

Dovresti usare invece scaleY.

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>

Ho creato una versione con prefisso del fornitore del codice sopra su jsfiddle e ho cambiato il tuo jsfiddle per usare scaleY invece di height.


6
Sto votando questa risposta perché funziona bene nei browser compatibili con css3, ma va notato che questo non funzionerà affatto in IE <9 e non sarà animato (salterà semplicemente) in IE <10
Hailwood,

215
Questo metodo ottiene solo parzialmente l'effetto desiderato ma in realtà non rimuove lo spazio. La scatola trasformata si comporta come un elemento relativamente posizionato: lo spazio viene occupato indipendentemente da come viene ridimensionato. Dai un'occhiata a questo jsFiddle che prende il tuo primo e aggiunge solo un testo fasullo in fondo. Nota come il testo sottostante non si sposta verso l'alto quando l'altezza della casella viene ridotta a zero.
animuson

9
Ora lo fa: jsfiddle.net/gNDX3/1 Fondamentalmente devi modellare i tuoi elementi in base a ciò di cui hai bisogno. Non ci sono proiettili d'argento o widget come comportamento in CSS / HTML.
dotnetCarpenter il

70
Mentre applaudo qualcuno che sta provando approcci diversi, l'effetto del mondo reale e le complicazioni che questa soluzione comporta è molto peggio del già terribile hack di altezza massima. Per favore non usare
mystrdat,

2
@SquareCat va bene tutto bene. Eccone uno che è più simile a un cassetto: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter

202

Al momento non puoi animare in altezza quando una delle altezze coinvolte è auto, devi impostare due altezze esplicite.


12
Di solito esistono diversi modi per risolvere un problema e non tutti sono appropriati o possibili. Non so perché @JedidiahHurt e Ryan Ore stanno cercando di limitare le possibili risposte su un sito di domande e risposte. Soprattutto se si considera che questa risposta è stata qui prima. Dubbiamente, poiché la risposta accettata è una soluzione alternativa.
Evviva Sto aiutando il

5
La risposta di @jake non è né elegante né corretta. La risposta collegata qui è di gran lunga migliore. Funziona correttamente e gestisce casi di tutte le dimensioni - nessuno dei due può dire la risposta accettata.
GraphicsMuncher,

5
Sì: lo fathis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy,

4
Questo è programmato per essere risolto in qualche modo? Voglio dire, è abbastanza naturale aspettarsi di poter passare da 0 altezza a auto...
Augustin Riedinger,

6
@Meliodas "no / you can" è una risposta valida e accettabile alle domande su Stack Overflow.
TylerH,

122

La soluzione che ho sempre usato è stato quello di prima fuori dissolvenza, quindi ridurre i font-size, paddinge marginvalori. Non sembra lo stesso di un panno, ma funziona senza un statico heighto max-height.

Esempio funzionante:

/* 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>


Ha funzionato perfettamente per me. Non del tutto simile a jQuery slideUp / Down. Puoi vedere una differenza solo con CPU deboli, come vecchi computer o dispositivi mobili, e aprendone e chiudendone molte contemporaneamente.
Cruz Nunez,

3
Se hai pulsanti o altri elementi non testuali, finirai con uno spazio bianco indesiderato senza passare il mouse.
Dennis W,

3
Puoi perfezionarlo ulteriormente selezionando tutti gli elementi figlio *e applicando altre modifiche. I pulsanti avrebbero dovuto essere influenzati dal mio codice sopra, tuttavia, se fossero stati correttamente disegnati em.
Steven Vachon,

1
Nel mio caso, avevo anche bisogno di aggiungere line-height: 0;eborder-width: 0;
Andrey Shatilov il

1
Non dovresti aver bisogno di aggiustare line-heightse eviti correttamente le unità sul valore: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon,

93

So che questa è la risposta a trentaquattro domande a questa domanda, ma penso che ne valga la pena, quindi ecco qui. Questa è una soluzione solo CSS con le seguenti proprietà:

  • Non vi è alcun ritardo all'inizio e la transizione non si interrompe in anticipo. In entrambe le direzioni (espansione e compressione), se si specifica una durata di transizione di 300 ms nel CSS, la transizione richiede 300 ms, punto.
  • Sta spostando l'altezza effettiva (a differenza transform: scaleY(0)), quindi fa la cosa giusta se ci sono contenuti dopo l'elemento pieghevole.
  • Mentre (come in altre soluzioni) ci sono numeri magici (come "scegli una lunghezza superiore a quella che sarà mai la tua scatola"), non è fatale se il tuo presupposto finisce per essere sbagliato. La transizione potrebbe non sembrare sorprendente in quel caso, ma prima e dopo la transizione, questo non è un problema: nello stato espanso ( height: auto), l'intero contenuto ha sempre l'altezza corretta (a differenza, ad esempio, se scegli una max-heightche risulta essere troppo basso). E nello stato collassato, l'altezza è zero come dovrebbe.

dimostrazione

Ecco una demo con tre elementi pieghevoli, tutti di diverse altezze, che usano tutti lo stesso CSS. Potresti voler fare clic su "pagina intera" dopo aver fatto clic su "esegui snippet". Si noti che JavaScript attiva o disattiva solo la collapsedclasse CSS, non sono previste misurazioni. (È possibile eseguire questa dimostrazione esatta senza JavaScript utilizzando una casella di controllo o :target). Inoltre, la parte del CSS responsabile della transizione è piuttosto breve e l'HTML richiede solo un singolo elemento wrapper aggiuntivo.

$(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>

Come funziona?

Ci sono infatti due transizioni coinvolte nel realizzare questo. Uno di questi passa margin-bottomdallo 0px (nello stato espanso) allo -2000pxstato compresso (simile a questa risposta ). Il 2000 qui è il primo numero magico, si basa sul presupposto che la tua casella non sarà più alta di questa (2000 pixel sembrano una scelta ragionevole).

L'uso della margin-bottomsola transizione da solo ha due problemi:

  • Se in realtà hai una scatola più alta di 2000 pixel, allora margin-bottom: -2000pxnon nasconderai tutto: ci saranno oggetti visibili anche nel caso compresso. Questa è una correzione minore che faremo più tardi.
  • Se la casella effettiva è, diciamo, alta 1000 pixel e la tua transizione è lunga 300 ms, allora la transizione visibile è già finita dopo circa 150 ms (o, nella direzione opposta, inizia 150ms in ritardo).

Risolvendo questo secondo problema è dove entra in gioco la seconda transizione, e questa transizione mira concettualmente all'altezza minima del wrapper ("concettualmente" perché non stiamo effettivamente usando la min-heightproprietà per questo; ne parleremo più avanti).

Ecco un'animazione che mostra come combinare la transizione del margine inferiore con la transizione dell'altezza minima, entrambe di uguale durata, ci dia una transizione combinata da tutta altezza a zero altezza che ha la stessa durata.

animazione come descritto sopra

La barra di sinistra mostra come il margine inferiore negativo spinge il fondo verso l'alto, riducendo l'altezza visibile. La barra centrale mostra come l'altezza minima garantisce che nel caso del collasso, la transizione non finisca presto e, nel caso in espansione, la transizione non inizi in ritardo. La barra di destra mostra come la combinazione delle due fa sì che la scatola passi dalla piena altezza a zero altezza nel tempo corretto.

Per la mia demo ho optato per 50px come valore di altezza minima superiore. Questo è il secondo numero magico e dovrebbe essere inferiore all'altezza della scatola. Anche 50px sembrano ragionevoli; sembra improbabile che tu voglia molto spesso rendere pieghevole un elemento che non sia nemmeno alto 50 pixel in primo luogo.

Come puoi vedere nell'animazione, la transizione risultante è continua, ma non differenziabile: nel momento in cui l'altezza minima è uguale all'altezza completa regolata dal margine inferiore, si verifica un improvviso cambio di velocità. Ciò è molto evidente nell'animazione perché utilizza una funzione di temporizzazione lineare per entrambe le transizioni e perché l'intera transizione è molto lenta. Nel caso reale (la mia demo in alto), la transizione richiede solo 300 ms e la transizione del margine inferiore non è lineare. Ho giocato con molte diverse funzioni di temporizzazione per entrambe le transizioni e quelle con cui ho finito mi sembrava che funzionassero meglio per la più ampia varietà di casi.

Rimangono due problemi da risolvere:

  1. il punto dall'alto, in cui le caselle di altezza superiore a 2000 pixel non sono completamente nascoste nello stato compresso,
  2. e il problema inverso, in cui nel caso non nascosto, le caselle di altezza inferiore a 50 pixel sono troppo alte anche quando la transizione non è in esecuzione, poiché l'altezza minima le mantiene a 50 pixel.

Risolviamo il primo problema dando l'elemento contenitore a max-height: 0nel caso compresso, con una 0s 0.3stransizione. Ciò significa che non è in realtà una transizione, ma max-heightviene applicata con un ritardo; si applica solo al termine della transizione. Perché ciò funzioni correttamente, dobbiamo anche scegliere un valore numerico max-heightper lo stato opposto, non compresso. Ma a differenza del caso 2000px, in cui la selezione di un numero troppo grande influisce sulla qualità della transizione, in questo caso non importa. Quindi possiamo solo scegliere un numero così alto che sappiamo che nessuna altezza potrà mai avvicinarsi a questo. Ho scelto un milione di pixel. Se ritieni di dover supportare contenuti di altezza superiore a un milione di pixel, 1) mi dispiace e 2) aggiungi solo un paio di zeri.

Il secondo problema è il motivo per cui non stiamo effettivamente utilizzando min-heightper la transizione di altezza minima. Invece, c'è uno ::afterpseudo-elemento nel contenitore con un heightche passa da 50px a zero. Questo ha lo stesso effetto di a min-height: Non consente al contenitore di ridursi al di sotto dell'altezza attualmente presente nello pseudo-elemento. Ma poiché stiamo usando height, ora non min-heightpossiamo usare max-height(ancora una volta applicato con un ritardo) per impostare l'altezza effettiva dello pseudo-elemento a zero una volta terminata la transizione, assicurando che almeno al di fuori della transizione, anche i piccoli elementi abbiano il altezza corretta. Perché min-heightè più forte di max-height, questo non funzionerebbe se usassimo il contenitore min-heightanziché gli pseudo-elementiheight. Proprio come max-heightnel paragrafo precedente, max-heightanche questo ha bisogno di un valore per l'estremità opposta della transizione. Ma in questo caso possiamo solo scegliere il 50px.

Testato su Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (ad eccezione di un problema di layout flexbox con la mia demo che non ho disturbato il debug) e Safari (Mac, iOS ). Parlando di flexbox, dovrebbe essere possibile farlo funzionare senza usare alcun flexbox; in effetti penso che potresti far funzionare quasi tutto in IE7 - tranne per il fatto che non avrai transizioni CSS, rendendolo un esercizio piuttosto inutile.


36
Vorrei che tu potessi accorciare (semplificare) la tua soluzione, o almeno l'esempio di codice - sembra che il 90% dell'esempio di codice non sia pertinente per la tua risposta.
Gil Epshtain,

C'è un modo per far funzionare anche queste transizioni se collapsible-wrapperha una posizione assoluta. L' overflow: hiddenelemento on è richiesto per la transizione ma interferisce con elementi assolutamente posizionati.
pmkro

1
@pmkro Sì, ho avuto anche quel problema. L'ho risolto usando un clip-path: polygon(...)invece di overflow: hidden, perché a differenza del secondo, puoi passare al primo. Come fallback per i browser più vecchi ho usato una trasformazione di ridimensionamento aggiuntiva. Vedi il commento nella parte superiore di github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha

2
Bello. Funziona davvero bene. Dovrebbe essere la risposta accettata
Henry Ing-Simmons,

84

Puoi, con un po 'di scherzo non semantico. Il mio approccio abituale è quello di animare l'altezza di un DIV esterno che ha un singolo figlio che è un DIV senza stile utilizzato solo per misurare l'altezza del contenuto.

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>

Uno vorrebbe solo essere in grado di rinunciare a .measuringWrappere semplicemente impostare l'altezza del DIV su auto e avere quell'animazione, ma ciò non sembra funzionare (l'altezza viene impostata, ma non si verifica alcuna animazione).

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>

La mia interpretazione è che per l'esecuzione dell'animazione è necessaria un'altezza esplicita. Non è possibile ottenere un'animazione in altezza quando l'altezza (l'altezza iniziale o finale) è auto.


10
Dal momento che questo si basa su javascript, puoi anche aggiungere facilmente il misuratore di peso usando anche javascript!
Quickredfox,

18
Puoi farlo senza wrapper. Solo: funzione growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
user1742529

8
Dai un'occhiata ... parla di una semplice risposta jsfiddle.net/n5XfG/2596 ... La tua risposta è follemente complessa senza una buona ragione. Non è necessario max-height, non è necessario autoe soprattutto non è necessario Javascript! Solo due semplici dichiarazioni
VIDesignz,

12
@VIDesignz Animate oltre il margine: 100%. Questo è il 100% della larghezza dell'elemento, non la sua altezza! Ciò significa che se hai un elemento con un'altezza maggiore della sua larghezza, questo non lo nasconderà. Inoltre, la velocità di animazione effettiva è totalmente diversa da quella definita.
Timo Türschmann,

3
Ero entusiasta della risposta di VIDesignz fino a quando non ho letto di più sul commento di Timo. Sì, margin-top(e margin-bottom) sono relativi alla larghezza quando definiti in percentuale, sì, è stupido, ma spiega anche perché i tempi di animazione di apertura e chiusura sono completamente diversi: è la stessa cosa che vedi nella risposta più votata, specificando un valore altezza massima codificata. Perché è così difficile fare bene?
Coderer,

52

Una soluzione visiva per animare l'altezza usando le transizioni CSS3 è invece animare il riempimento.

Non si ottiene l'effetto di cancellazione completo, ma giocare con i valori di durata della transizione e di riempimento dovrebbe avvicinarsi abbastanza. Se non vuoi impostare esplicitamente altezza / altezza massima, questo dovrebbe essere quello che stai cercando.

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/ (risolto da stephband sopra jsFiddle)


3
Penso che questa sia la risposta migliore. Questa tecnica può estendersi anche alla regolazione dell'imbottitura sui bambini. Nel mio caso sono stato in grado di combinarlo con transizioni di altezza esplicite su alcuni dei contenuti che vengono rivelati e l'effetto totale ha molto più successo della soluzione per l'altezza massima, che va bene per la rivelazione ma introduce un momento imbarazzante di ritardo su hide. Presto non mi animerei affatto di introdurre un ritardo insignificante che rende la mia app non rispondente. Sono sorpreso che così tante persone sembrano pensare che sia accettabile.
Punto

17
Solo che qui non stai animando affatto l'altezza. Stai animando il riempimento ... può scomparire bene perché può animare dallo stato corrente fino a 0, ma se guardi attentamente quando si espande si apre con il testo e quindi il riempimento si anima solo .. perché non lo fa so come animare da 0 ad auto .... ha bisogno di un intervallo numerico ... ecco come funziona l'interpolazione.
Rob R

Questa è una buona soluzione che non è confusa. Non è la migliore risposta a questa domanda, ma è un'alternativa che ha funzionato per me. A volte hai solo bisogno dell'effetto di qualche animazione in altezza, non dell'animazione completa :)
Ivan Durst

Questa soluzione fallisce anche quando si utilizza un ritardo di transizione.
Michel Joanisse,

Concordo anche sul fatto che questa soluzione è una soluzione chiara che fornisce transizioni abbastanza fluide se l'animazione è abbastanza veloce (nel mio caso per una fisarmonica con transizioni di 0.1s è perfetto!). Tuttavia, non si adatta a molte applicazioni, ma nemmeno gli altri risponderanno a questa domanda!
Remi D,

46

La mia soluzione alternativa è quella di trasferire l'altezza massima all'altezza esatta del contenuto per una piacevole animazione fluida, quindi utilizzare una transizioneEnd callback per impostare l'altezza massima su 9999px in modo che il contenuto possa ridimensionare liberamente.

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>


14
@ Derija93 Questo utilizza semplici animazioni di timeout javascript. La sfida è invece utilizzare le transizioni CSS3.
Adam,

3
Beh si. Ma perché dovresti utilizzare invece "Transizioni CSS3" se, nel tuo esempio, stai già utilizzando jQuery, una libreria che fornisce comunque molto codice "scrivi meno, fai di più"? Volevo sottolineare che la tua versione potrebbe apparire molto meglio, anche se più complicata, se NON usasse jQuery in modo che potesse funzionare praticamente su qualsiasi sito web. Immagino di averlo scritto molto male. Mi dispiace per quello. ;)
Kiruse,

27
@ Derija93 Forse perché le transizioni CSS3 hanno (secondo tutte le fonti che riesco a trovare) prestazioni molto migliori rispetto alle animazioni jQuery. (In realtà è estremamente importante per il mio caso d'uso corrente, che mi ha portato qui.)
Mark Amery,

4
@ Derija93 'Cause Le animazioni Javascript funzionano lentamente rispetto alle transizioni CSS3 fratello e con le animazioni jQuery devi preoccuparti del ciclo di animazione. Animare qualcosa al clic, quindi fare clic rapidamente e guardare mentre le animazioni si ripetono. Questo è gestito con CSS3.
AlbertEngelB,

2
@ Dropped.on.Caprica Questo è un po 'vecchio ora. Ho già detto di aver frainteso il concetto che stava cercando di dimostrare. Ad ogni modo, per animazioni semplici, .stoprisolve il problema piuttosto complesso del loop di animazione. Ora, quando si animano l'altezza e il colore ma si desidera interrompere solo l'animazione dell'altezza, le cose diventano un po 'più complicate ... Capisco.
Kiruse

43

La risposta accettata funziona nella maggior parte dei casi, ma non funziona bene quando si divpuò variare notevolmente in altezza - la velocità di animazione non dipende dall'altezza effettiva del contenuto e può apparire instabile.

Puoi comunque eseguire l'animazione effettiva con CSS, ma devi usare JavaScript per calcolare l'altezza degli elementi, invece di provare a usare auto. Non è richiesto jQuery, anche se potrebbe essere necessario modificarlo un po 'se si desidera la compatibilità (funziona nell'ultima versione di 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>


Questo potrebbe essere utile, ma non posso dirlo perché non so come usarlo senza un esempio migliore.
Ivan Durst,

1
Basta passare qualsiasi elemento DOM (ad esempio, da document.getElementById('mydiv')o $('#mydiv').get()) e la funzione commuta tra nasconderlo / mostrarlo. Se imposti le transizioni CSS, l'elemento si animerà automaticamente.
Oleg Vaskevich,

Ho aggiunto uno snippet. Questo ha il vantaggio di funzionare bene per contenuti di dimensioni dinamiche.
Oleg Vaskevich,

3
-1, dal momento che questo non "utilizza CSS". Il punto della domanda è per una soluzione priva di JS. Penso che la risposta "corretta", che non è un trucco, sarebbe questa, però ...
Dudewad,

16
Downvote per l'utilizzo di JS - sul serio? La metà delle risposte qui usa JS, quindi il tuo voto negativo non sembra giusto o necessario. Inoltre, questa risposta utilizza ancora CSS per eseguire l'animazione effettiva, che l'IMO era il punto dell'OP. L'unica cosa per cui viene utilizzato JS è calcolare l'altezza effettiva, poiché è impossibile farlo con CSS puro (e l'impostazione min-heightcome nella risposta prevista provoca problemi con la velocità di animazione quando la dimensione dell'elenco può variare notevolmente).
Oleg Vaskevich,

30

C'era poca menzione della Element.scrollHeightproprietà che può essere utile qui e può ancora essere utilizzata con una transizione CSS pura. La proprietà contiene sempre l'altezza "completa" di un elemento, indipendentemente dal fatto che e come il suo contenuto trabocca a causa dell'altezza compressa (ad es height: 0.).

Come tale, per un height: 0elemento (effettivamente completamente collassato), la sua altezza "normale" o "piena" è ancora prontamente disponibile attraverso il suo scrollHeightvalore (invariabilmente una lunghezza di pixel ).

Per un tale elemento, supponendo che abbia già impostato la transizione come ad es. (Usando ulcome da domanda originale):

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

Possiamo attivare l '"espansione" animata desiderata dell'altezza, usando solo CSS, con qualcosa di simile al seguente (supponendo che la ulvariabile si riferisca all'elenco):

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

Questo è tutto. Se è necessario comprimere l'elenco, una delle due seguenti affermazioni farà:

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

Il mio caso d'uso particolare ruotava attorno ad animare elenchi di lunghezze sconosciute e spesso considerevoli, quindi non mi sentivo a mio agio con un "abbastanza grande" arbitrario heighto max-heightspecifiche e rischiando il contenuto interrotto o il contenuto che devi improvvisamente scorrere (se overflow: auto, per esempio) . Inoltre, l'allentamento e i tempi sono interrotti con max-heightsoluzioni basate su, poiché l'altezza utilizzata può raggiungere il suo valore massimo molto prima di quanto sarebbe necessario per max-heightraggiungere 9999px. E man mano che le risoluzioni dello schermo aumentano, lunghezze di pixel come 9999pxlasciano un cattivo sapore in bocca. Questa particolare soluzione risolve il problema in modo elegante, secondo me.

Infine, qui si spera che le future revisioni degli autori di indirizzi CSS debbano fare questo tipo di cose in modo ancora più elegante - rivisitare la nozione di valori "calcolati" vs "usati" e "risolti" e considerare se le transizioni debbano essere applicate ai calcolati valori, comprese le transizioni con widthe height(che attualmente ricevono un po 'di trattamento speciale).


6
Non l'hai trovato nelle altre risposte qui perché l'interazione con il DOM utilizzando la Element.scrollHeightproprietà richiede JavaScript e, come afferma chiaramente la domanda, vogliono farlo senza JavaScript.
TylerH,

3
Ci sono molte altre parti di CSS diverse da quelle due pseudo-classi che possono risolvere questo problema, come mostrano le risposte più votate a pagina 1. L'impostazione di uno stile in JavaScript non è "puro CSS" poiché richiede innanzitutto che JS sia impostato. Spesso quando una persona richiede una soluzione solo CSS, è perché non sono in grado di iniettare JavaScript o non sono autorizzati dal loro progetto o ruolo attuale.
TylerH,

29

Utilizzare max-heightcon diversi passaggi di andamento e ritardo per ciascuno stato.

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

Vedi esempio: http://jsfiddle.net/0hnjehjc/1/


12
Il problema qui è, per assicurarti di avere abbastanza spazio in un ambiente dinamico, devi usare ridicole altezze simili 999999px. Questo, d'altra parte, sposterà l'animazione, perché i frame sono calcolati da questo valore. Quindi potresti finire con un "ritardo" di animazione in una direzione e una fine molto rapida nell'altra direzione (corr: funzioni di temporizzazione)
Julian F. Weinert,

29

Nessun valore codificato.

Nessun JavaScript.

Nessuna approssimazione

Il trucco è usare un sistema nascosto e duplicato divper far capire al browser cosa significa 100%.

Questo metodo è adatto ogni volta che sei in grado di duplicare il DOM dell'elemento che desideri animare.

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


Ben fatto: questa è una soluzione valida a un problema che in realtà non dovrebbe esistere. Animare l'altezza massima è bas a meno che la transizione non sia impostata su "lineare", inoltre è (ovviamente) molto male per l'animazione del contenuto CMS in cui l'altezza è variabile.
M_Willett,

è intelligente, ma non mi farebbe comodo duplicare elementi e contenuti DOM per questo.
Andre Figueiredo,

Puoi farlo senza contenitore e più performante animando transform: scaleY(1), poiché questa transizione non rimuove lo spazio.
Fabian von Ellerts,

21

Mentre lo pubblico ci sono già oltre 30 risposte, ma sento che la mia risposta migliora sulla risposta già accettata da jake.

Non ero contento del problema che deriva dal semplice utilizzo max-heighte dalle transizioni CSS3, poiché come molti commentatori hanno notato, devi impostare il tuo max-heightvalore molto vicino all'altezza effettiva o otterrai un ritardo. Vedi questo JSFiddle per un esempio di quel problema.

Per ovviare a questo (pur continuando a non utilizzare JavaScript), ho aggiunto un altro elemento HTML che transita il transform: translateYvalore CSS.

Questo mezzo sia max-heighte translateYda utilizzare sono: max-heightpermette l'elemento a spingere verso il basso gli elementi di sotto di essa, mentre translateYdà l'effetto "immediato" vogliamo. Il problema max-heightesiste ancora, ma il suo effetto è ridotto. Ciò significa che puoi impostare un'altezza molto più grande per il tuo max-heightvalore e preoccuparti di meno.

Il vantaggio generale è che sulla transizione (il collasso) l'utente vede translateYimmediatamente l' animazione, quindi non importa quanto tempo max-heightimpiega.

Soluzione come violino

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>


Bel confronto @davidtaubmann, la tua penna illustra molto bene le differenze! Sono andato con translateYpiù di scalequanto non mi piaceva come scaledato che effetto di compressione.
Andrew Messier,

Grazie @ andrew-messier, ho eliminato il commento perché c'è un errore, translateY non sta eseguendo in codepen.io/davidtaubmann/pen/zKZvGP (perché non c'è nessun bambino a fare la mossa, ha bisogno di una correzione) ... ma in questo uno possiamo vedere perfettamente il confronto SOLO da MAX-HEIGHT e mescolarlo con la scala (come indicato su altre risposte): codepen.io/davidtaubmann/pen/jrdPER . Tutto ciò mi ha aiutato molto con una metodologia Target che sto usando
David Taubmann,

18

Ok, quindi penso di aver trovato una risposta semplicissima ... no max-height, usa il relativeposizionamento, lavora sugli lielementi ed è puro CSS. Non ho testato altro che Firefox, anche se a giudicare dai CSS, dovrebbe funzionare su tutti i browser.

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>

13
Questo funzionerà fino a quando l'altezza dell'elemento non supera la sua larghezza. È la base di come vengono calcolati i margini usando le percentuali: sono calcolati in base alla larghezza dell'elemento. Quindi se hai un elemento largo 1000 px, un elemento a 1100 px sarà troppo grande per far funzionare questa soluzione, il che significa che dovresti aumentare quel margine superiore negativo. Fondamentalmente, è esattamente lo stesso problema dell'utilizzo dell'altezza o dell'altezza massima.
Dudewad,

15

EDIT: Scorri verso il basso per una risposta aggiornata
Stavo facendo un elenco a discesa e ho visto questo post ... molte risposte diverse ma decido di condividere anche il mio elenco a discesa, ... Non è perfetto ma almeno utilizzerà solo css per cadere in picchiata! Ho usato transform: translateY (y) per trasformare l'elenco in vista ...
Puoi vedere di più nel test
http://jsfiddle.net/BVEpc/4/
Ho inserito div dietro ogni li perché il mio l'elenco a discesa proviene dall'alto e per mostrare correttamente ciò era necessario, il mio codice div è:

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

e hover è:

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

e poiché ul height è impostato sul contenuto che può superare il contenuto del tuo corpo, ecco perché l'ho fatto per ul:

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

e passa il mouse:

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

la seconda volta dopo la transizione è in ritardo e si nasconderà dopo che il mio elenco a discesa è stato chiuso in modo animato ...
Spero che qualcuno ne tragga beneficio.

EDIT: Non riesco proprio a credere che ppl stia effettivamente usando questo prototipo! questo menu a discesa è solo per un sottomenu ed è tutto !! Ne ho aggiornato uno migliore che può avere due sottomenu per entrambe le direzioni RTL e RTL con supporto IE 8.
Fiddle for LTR
Fiddle for RTL si
spera che qualcuno lo trovi utile in futuro.


11

Puoi passare da altezza: 0 ad altezza: auto a condizione che tu fornisca anche altezza minima e altezza massima.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

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

@Stuart Badminton puoi fornire un esempio funzionante? L'ho messo insieme, ma non sembra funzionare da nessuna parte :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin,

5
Il problema è con la tua regola di transizione. per farlo funzionare è necessario applicare anche la transizione all'altezza minima e massima. transizione: altezza .5s lineare, altezza minima .5s lineare, altezza massima .5s lineare
Stuart Badminton

2
ecco cosa ho più tardi, proprio come hai detto, per animare l'altezza massima: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin

11
Certo, c'è un problema con questo. Il tempo necessario per animare l'altezza massima non è il tempo necessario per passare all'altezza automatica della casella: arriva all'altezza: auto prima, prima che la massima altezza abbia terminato l'animazione. Allo stesso modo, quando si passa a 0, la transizione non inizia immediatamente, poiché l'altezza massima inizia ad un'altezza maggiore dell'altezza: auto. Transizione all'altezza massima: 1000px lo chiariscono: jsfiddle.net/n5XfG/11
stephband

Hai ragione, su ulteriori test ho notato questo. Non so se un'altezza completa: da 0 ad altezza: l'auto sarà mai implementata, ma l'uso di un'altezza massima in alcune situazioni risolve una situazione altrimenti impossibile usando solo CSS.
Stuart Badminton,

10

Soluzione Flexbox

Professionisti:

  • semplice
  • no JS
  • transizione graduale

Contro:

  • l'elemento deve essere inserito in un contenitore flessibile ad altezza fissa

Il modo in cui funziona è avere sempre la base flessibile: auto sull'elemento con contenuto e invece transizione flex-grow e flex-shrink.

Modifica: JS Fiddle migliorato ispirato all'interfaccia 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


7

Espandendosi sulla risposta di @Jake, la transizione si sposterà fino al valore di altezza massima, causando un'animazione estremamente veloce - se imposti le transizioni per entrambi: passa il mouse e off puoi quindi controllare un po 'di più la folle velocità.

Quindi il li: hover è quando il mouse entra nello stato e quindi la transizione sulla proprietà non hovered sarà il congedo del mouse.

Spero che questo possa essere di qualche aiuto.

per esempio:

.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 */

Ecco un violino: http://jsfiddle.net/BukwJ/


1
Non so perché questo abbia un -1. In realtà penso che questo sia un tentativo migliore di alcune persone che aggiungono un sacco di javascript. Non è una soluzione perfetta, ma con alcune modifiche fa un buon lavoro, ho scoperto che funziona per il valore di ritorno. transizione: tutti i 500ms cubici-bezier (0.000, 1.225, 0.085, 0.385); Questo si basava su una transizione 2s all'apertura, quindi il codice sopra riportato per tornare allo stato iniziale. Grazie ... Questo mi ha aiutato meglio di tutto quanto sopra. +1 Ho usato questo sito per creare il bezier matthewlein.com/ceaser
gcoulby,

OK, ci sono voluti un po 'più di modifiche. transizione: tutti i 500ms cubi-bezier (0.000, 1.390, 0.000, 0.635); Dovrei specificare che la mia transizione va dal 5% al ​​100% (ma in realtà il valore finale è di circa il 28%), quindi dipende dal progetto.
gcoulby,

Assolutamente, non è l'ideale, ma è una soluzione rapida se sai intorno a quale sarà l'altezza media.
Jamie,

7

Penso di aver trovato una soluzione davvero solida

OK! So che questo problema è vecchio come Internet ma penso di avere una soluzione che ho trasformato in un plugin chiamato mutant-transizione . La mia soluzione imposta gli style=""attributi per gli elementi tracciati ogni volta che si verifica una modifica nel DOM. il risultato finale è che puoi usare un buon CSS ole per le tue transizioni e non usare correzioni hacky o javascript speciali. L'unica cosa che devi fare è impostare ciò che vuoi tracciare sull'elemento in questione usando data-mutant-attributes="X".

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

Questo è tutto! Questa soluzione utilizza MutationObserver per seguire le modifiche nel DOM. Per questo motivo, non è necessario impostare nulla o utilizzare JavaScript per animare manualmente le cose. Le modifiche vengono tracciate automaticamente. Tuttavia, poiché utilizza MutationObserver, questo passerà solo in IE11 +.

Fiddles!


12
Di solito, quando qualcuno chiede come fare qualcosa "senza JavaScript", significa che la soluzione deve funzionare su browser con JavaScript disabilitato. Non significa "senza scrivere i miei script". Quindi dire che il tuo plugin può essere usato senza usare JavaScript è fuorviante nella migliore delle ipotesi - considerando che si tratta di un plugin JavaScript.
BoltClock

Dovrei chiarire, quando dico che non devi usare alcun javascript speciale, intendo che non devi scrivere alcun javascript. includi semplicemente la libreria JS e specifica quali attributi vuoi guardare nell'HTML. Non è necessario utilizzare css ad altezza fissa o capire qualcosa. solo stile e via.
JonTroncoso,

"(...) non devi davvero impostare nulla o utilizzare javascript per animare manualmente le cose (...)" quindi stai usando JavaScript per divertimento
Roko C. Buljan

6

Ecco un modo per passare da qualsiasi altezza iniziale, incluso 0, ad auto (full size e flessibile) senza richiedere il codice impostato su base per nodo o qualsiasi codice utente da inizializzare: https://github.com/csuwildcat / transizione-auto . Questo è fondamentalmente il Santo Graal per quello che vuoi, credo -> http://codepen.io/csuwldcat/pen/kwsdF . Basta schiaffeggiare il seguente file JS nella tua pagina e tutto ciò che devi fare è aggiungere / rimuovere un singolo attributo booleano - reveal=""- dai nodi che desideri espandere e contrarre.

Ecco tutto ciò che devi fare come utente, una volta incluso il blocco di codice che si trova sotto il codice di esempio:

/*** 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>

Ecco il blocco di codice da includere nella tua pagina, dopodiché è tutto sugo:

Rilascia questo file JS nella tua pagina: è tutto Just Works ™

/ * Codice per altezza: auto; transizione * /

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

/ * Codice per 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);

9
Mentre questa è una soluzione molto più pulita e flessibile rispetto alle altre che sono state suggerite, personalmente credo che questo non dovrebbe essere qualcosa che JS dovrebbe toccare affatto. Non dicendo che ti sbagli, è solo un sospiro.
mystrdat,

Per essere chiari @mystrdat, JS viene utilizzato solo per verificare determinate condizioni e aggiungere un paio di attributi / valori CSS temporanei in base agli eventi / condizioni che sta osservando. La transizione è ancora tutta eseguita nei CSS. Anche se sono d'accordo, è triste che questo livello di installazione e gestione delle mani sia necessario per soddisfare veramente un caso d'uso apparentemente semplice! : /
csuwldcat,

1
@KarolisRamanauskas no, questo non è un problema: IE supporta sia i keyframe CSS sia requestAnimationFrame. Stavo usando clipcome trigger di proprietà fittizio e per qualche motivo IE ha smesso di consentire l'animazione della clip. Sono passato all'opacità e tutto funziona di nuovo: codepen.io/csuwldcat/pen/FpzGa . Ho aggiornato il codice nella risposta affinché corrisponda.
csuwldcat,

1
@csuwldcat Soluzione molto bella, ho notato che il contenuto appare e scompare per una piccola frazione, perché succede? È evitabile?
Beto Aveiga,

12
Voglio votare questo, ma invece di rispondere alla domanda su come farlo funzionare, hai condiviso un plugin che hai scritto per farlo funzionare. Noi, i curiosi, siamo lasciati a decodificare il tuo plugin, il che non è molto divertente. Vorrei che aggiornassi la tua risposta per contenere ulteriori spiegazioni su cosa fa il tuo plugin e perché. Cambia il codice per essere più esplicativo. Ad esempio, hai un'intera sezione di codice che scrive semplicemente CSS statici. Preferirei vedere il CSS, piuttosto che il codice che lo genera. Puoi tralasciare le parti noiose, come ripetere per tutti i prefissi del browser.
gilly3,

6

La risposta di Jake per animare l'altezza massima è ottima, ma ho trovato fastidioso il ritardo causato dall'impostazione di una grande altezza massima.

Si potrebbe spostare il contenuto comprimibile in un div interno e calcolare l'altezza massima ottenendo l'altezza del div interno (tramite JQuery sarebbe il Hightight ()).

$('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');
});

Ecco un link jsfiddle: http://jsfiddle.net/pbatey/duZpT

Ecco un jsfiddle con la quantità minima assoluta di codice richiesta: http://jsfiddle.net/8ncjjxh8/


5

Mi rendo conto che questa discussione sta invecchiando, ma si colloca in alto su alcune ricerche di Google, quindi immagino che valga la pena aggiornarla.

Ottieni anche / imposta l'altezza dell'elemento:

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

Dovresti scaricare questo Javascript immediatamente dopo il tag di chiusura di target_box in un tag di script incorporato.


document.getElementById ('target_box'). getBoundingClientRect (). height dovrebbe fare meglio.
modifica l'

5

Sono stato in grado di farlo. Ho un .child& a .parentdiv. Il div bambino si adatta perfettamente alla larghezza / altezza del genitore con il absoluteposizionamento. Quindi anima la translateproprietà per spingerne il Yvalore verso il basso 100%. La sua animazione molto fluida, senza difetti o lati negativi come qualsiasi altra soluzione qui.

Qualcosa del genere, pseudo codice

.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 */
}

Si potrebbe specificare un heightsu .parent, in px, %o congedo come auto. Questo div quindi maschera il .childdiv quando scorre verso il basso.


L'esempio di lavoro di questo sarà molto apprezzato.
Neurotrasmettitore

5

Ho pubblicato una risposta con un po 'di JavaScript e ho ottenuto il downgrade, quindi mi sono infastidito e riprovato, e l'ho risolto solo con CSS!

Questa soluzione utilizza alcune "tecniche":

  • padding-bottom:100%'hack' in cui le percentuali sono definite in termini di larghezza corrente dell'elemento. Maggiori informazioni su questa tecnica.
  • float shrink-wrapping, (che richiede un div extra per applicare l'hack di cancellazione del float)
  • uso non generico di https://caniuse.com/#feat=css-writing-mode e alcune trasformazioni per annullarlo (questo consente l'uso dell'hacking di padding sopra in un contesto verticale)

Il risultato però è che otteniamo una transizione performante usando solo CSS e una singola funzione di transizione per ottenere una transizione senza problemi; Il Sacro Graal!

Certo, c'è un aspetto negativo! Non riesco a capire come controllare la larghezza alla quale il contenuto viene tagliato ( overflow:hidden); a causa dell'hack sul fondo dell'imbottitura, la larghezza e l'altezza sono intimamente correlate. Potrebbe esserci un modo, quindi tornerà ad esso.

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>


L'altro aspetto negativo di questo è che non è possibile spostare il cursore dal div "hoverme" sull'elenco degli elementi per interagire con essi (apparentemente il motivo della richiesta di OP), quindi è utile solo come una descrizione comando anziché , ad esempio, un menu con sottomenu nidificati.
TylerH,

Questa è puramente una conseguenza del modo in cui la domanda è stata formulata e non è correlata alla tecnica. È possibile modificare il trigger in modo che sia un input[type="checkbox"]:checkedinvece che :hoverse lo si desidera (ovvero non si desidera utilizzare JavaScript per aggiungere classi)
EoghanM

Voglio dire, indipendentemente da come è formulata la domanda, se l'implementazione di una soluzione è così limitata, dovrebbe probabilmente essere menzionata nella risposta di quella soluzione (o preferibilmente spiegata ). Dici che questa soluzione è un "santo graal" ma il div in transizione non può nemmeno essere sospeso sopra ... non mi sembra una soluzione così definitiva per me.
TylerH,

Ciao @TylerH, mi scuso, ho perso il fatto che il trigger nella domanda originale circonda l'elenco espanso, quindi l'elenco dovrebbe rimanere aperto quando passa il mouse sopra. Ho aggiornato la mia risposta per riflettere questo. Come ho già detto, tuttavia, come sia innescato non è poi così germano, poiché sto presentando una nuova tecnica che in precedenza non era ritenuta possibile solo nei CSS (sto cercando una soluzione da alcuni anni).
EoghanM,

4

Ecco una soluzione che ho appena usato in combinazione con jQuery. Funziona con la seguente struttura HTML:

<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>

e la funzione:

    $('#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);
    });

È quindi possibile utilizzare una funzione clic per impostare e rimuovere l'altezza utilizzando 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 }

questo non viene visualizzato correttamente con la versione jquery più recente
oliverbachmann,

4

Di recente ho spostato max-heightgli lielementi on anziché il wrapping ul.

Il ragionamento è che il ritardo per i piccoli max-heightsè molto meno evidente (se non del tutto) rispetto alle grandi max-heights, e posso anche impostare il mio max-heightvalore relativo al font-sizedel lipiuttosto che un numero enorme arbitrario utilizzando emso rems.

Se la mia dimensione del carattere è 1rem, imposterò il mio max-heightsu qualcosa di simile 3rem(per adattarsi al testo a capo). Puoi vedere un esempio qui:

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


3

Non ho letto tutto in dettaglio ma di recente ho riscontrato questo problema e ho fatto quanto segue:

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

Ciò ti consente di avere un div che all'inizio mostra un contenuto fino a 200 px di altezza e al passaggio del mouse la sua dimensione diventa almeno alta quanto l'intero contenuto del div. Il Div non diventa 3000px ma 3000px è il limite che sto imponendo. Assicurati di avere la transizione su non: hover, altrimenti potresti ottenere uno strano rendering. In questo modo il: hover eredita dal non: hover.

La transizione non funziona da px a% o ad auto. È necessario utilizzare la stessa unità di misura. Questo funziona bene per me. L'uso di HTML5 lo rende perfetto ....

Ricorda che c'è sempre un lavoro intorno ...; )

Spero che qualcuno lo ritenga utile


3

La soluzione di altezza massima di Jake funziona bene, se il valore di altezza massima codificato fornito non è molto più grande dell'altezza reale (perché altrimenti ci sono ritardi indesiderati e problemi di temporizzazione). D'altra parte, se il valore hardcoded accidentalmente non è maggiore dell'altezza reale, l'elemento non si aprirà completamente.

La seguente unica soluzione CSS richiede anche una dimensione hardcoded che dovrebbe essere più grande della maggior parte delle dimensioni reali che si verificano. Tuttavia, questa soluzione funziona anche se la dimensione reale è in alcune situazioni più grande della dimensione codificata. In tal caso la transizione potrebbe saltare un po ', ma non lascerà mai un elemento parzialmente visibile. Quindi questa soluzione potrebbe essere utilizzata anche per contenuti sconosciuti, ad esempio da un database, in cui sai solo che il contenuto non è in genere maggiore di x pixel, ma ci sono eccezioni.

L'idea è di usare un valore negativo per margin-bottom (o margin-top per un'animazione leggermente diversa) e posizionare l'elemento di contenuto in un elemento centrale con overflow: nascosto. Il margine negativo dell'elemento di contenuto riduce così l'altezza dell'elemento centrale.

Il codice seguente utilizza una transizione sul margine inferiore da -150 px a 0 px. Questo da solo funziona bene fintanto che l'elemento di contenuto non è superiore a 150px. Inoltre utilizza una transizione sull'altezza massima per l'elemento centrale da 0px al 100%. Questo infine nasconde l'elemento centrale se l'elemento di contenuto è superiore a 150px. Per l'altezza massima, la transizione viene utilizzata solo per ritardare di un secondo la sua applicazione in chiusura, non per un effetto visivo uniforme (e quindi può andare da 0px al 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>

Il valore per il margine inferiore dovrebbe essere negativo e il più vicino possibile all'altezza reale dell'elemento di contenuto. Se il suo (valore absoute) è maggiore, ci sono problemi di ritardo e temporizzazione simili a quelli delle soluzioni di altezza massima, che possono tuttavia essere limitati a condizione che la dimensione codificata non sia molto più grande di quella reale. Se il valore assoluto per margine inferiore è inferiore all'altezza reale, la concia salta un po '. In ogni caso dopo la transizione l'elemento di contenuto viene visualizzato o rimosso completamente.

Per maggiori dettagli vedi il mio post sul blog http://www.taccgl.org/blog/css_transition_display.html#combined_height


3

Puoi farlo creando un'animazione inversa (comprimi) con il percorso della clip.

#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>


2

Questa non è esattamente una "soluzione" al problema, ma più una soluzione alternativa. Funziona solo come scritto con il testo, ma è sicuro che può essere modificato per funzionare con altri elementi.

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

Ecco un esempio: http://codepen.io/overthemike/pen/wzjRKa

In sostanza, imposti la dimensione del carattere su 0 e la transizione al posto dell'altezza, o dell'altezza massima, o della scalaY () ecc. A un ritmo abbastanza rapido da far sì che l'altezza si trasformi in ciò che desideri. Trasformare l'altezza effettiva con CSS in auto non è attualmente possibile, ma trasformare il contenuto all'interno è, quindi la transizione della dimensione del carattere.

  • Nota: nel codice è presente JavaScript, ma l'unico scopo è aggiungere / rimuovere classi css al clic per la fisarmonica. Questo può essere fatto con i pulsanti di opzione nascosti, ma non ero concentrato su quello, solo sulla trasformazione dell'altezza.

1
Duplica efficacemente questa risposta , ma omette l'effetto di opacità-dissolvenza.
SeldomNeedy,

Concordato. Approccio migliore
Mike S.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.