Wie kann ich die Höhe ändern: 0; zur Höhe: auto; mit CSS?

2134

Ich versuche, <ul>mithilfe von CSS-Übergängen eine Folie nach unten zu erstellen.

Das <ul>beginnt um height: 0;. Beim Hover wird die Höhe auf eingestellt height:auto;. Dies führt jedoch dazu, dass es einfach erscheint und nicht übergeht.

Wenn ich es von height: 40px;bis mache height: auto;, rutscht es nach oben height: 0;und springt dann plötzlich auf die richtige Höhe.

Wie könnte ich das sonst ohne JavaScript tun?

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

Hagelholz
quelle
Ich glaube, die height:auto/max-heightLösung wird nur funktionieren, wenn Ihr erweiterter Bereich größer ist als der, den heightSie einschränken möchten. Wenn Sie eine haben max-heightvon 300px, aber ein Kombinationsfeld Dropdown - Liste , die zurückkehren 50px, dann max-heightwird dir nicht helfen, 50pxist variabel , abhängig von der Anzahl der Elemente, können Sie in eine unmögliche Situation kommen , wo ich es nicht reparieren kann , weil das heightnicht der Fall ist behoben, height:autowar die Lösung, aber ich kann keine Übergänge damit verwenden.
Christopher Thomas
51
OP versucht für CSS-Lösung, nicht js, sonst könnten sie nur Überlauf verwenden und animieren
Toni Leigh
5
@VIDesignz: Aber der obere Rand von -100% des inneren Div erhält die Breite des Wrapper-Div, nicht die Höhe. Diese Lösung hat also das gleiche Problem wie die Lösung mit maximaler Höhe. Wenn die Breite kleiner als die Höhe des Inhalts ist, wird nicht der gesamte Inhalt um -100% am oberen Rand ausgeblendet. Das ist also eine falsche Lösung.
GregTom
1
Siehe github.com/w3c/csswg-drafts/issues/626 für eine Diskussion über die Spezifikation und Implementierung einer geeigneten Lösung
Ben Creasy

Antworten:

2745

Verwenden Sie max-heightim Übergang und nicht height. Und setzen Sie einen Wert auf max-heightetwas Größeres, als Ihre Box jemals bekommen wird.

Eine weitere Antwort finden Sie in der JSFiddle-Demo von Chris Jordan .

#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
quelle
324
das funktioniert super! außer es gibt eine Verzögerung, wenn es startet, weil es für maximale Höhe startet, die anfangs sehr hoch ist .. hmm, ich denke, das ist etwas ärgerlich
vsync
175
+1 Tolle Lösung! Die berechnete Geschwindigkeit des Übergangs wird als die Zeit berechnet, die Sie für den Übergang zum Wert für die maximale Höhe angeben. Da die Höhe jedoch geringer als die maximale Höhe ist, erfolgt der Übergang zur tatsächlichen Höhe schneller (häufig erheblich) als der Zeit angegeben.
Kingjeffrey
50
Beachten Sie, dass dies zu einem hässlichen Übergangsende führen kann, wenn Sie Werte verwenden müssen, die viel größer als der tatsächlich berechnete Wert sind. Ich habe dies bemerkt, als ich versucht habe, ein Div von 0 auf die Inhaltshöhe zu bringen, die aufgrund unterschiedlicher Bildschirmgrößen stark variiert (2 Zeilen auf meinem 2560 x 1440-Monitor gegenüber> 10 Zeilen auf einem Smartphone). Dafür bin ich mit js gegangen.
Jpeltoniemi
44
Sehr hässliche Lösung, da sie eine Verzögerung in die eine, aber nicht in die andere Richtung erzeugt.
Tim
84
Dies ist eine ziemlich faule Lösung. Ich gehe davon aus, dass OP height: auto verwenden möchte, da die erweiterte Höhe des Containers etwas unvorhersehbar ist. Diese Lösung führt zu einer Verzögerung, bevor die Animation sichtbar wird. Außerdem ist die sichtbare Dauer der Animation nicht vorhersehbar. Sie erhalten viel besser vorhersehbare (und wahrscheinlich glattere) Ergebnisse, wenn Sie die kombinierte Höhe jedes der untergeordneten Containerknoten berechnen und dann auf einen genauen Höhenwert reduzieren.
Shawn Whinnery
314

Sie sollten stattdessen scaleY verwenden.

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>

Ich habe eine herstellerpräfixierte Version des obigen Codes für jsfiddle erstellt und Ihre jsfiddle so geändert , dass scaleY anstelle von height verwendet wird.

dotnetCarpenter
quelle
6
Ich stimme dieser Antwort zu, weil sie in css3-übergangsfähigen Browsern gut funktioniert, aber es sollte beachtet werden, dass dies in IE <9 überhaupt nicht funktioniert und in IE <10 nicht animiert wird (es springt nur)
Hailwood
215
Dieses Verfahren erreicht nur teilweise die gewünschte Wirkung , aber nicht wirklich entfernt den Raum. Die transformierte Box wirkt wie ein relativ positioniertes Element - der Raum wird unabhängig von seiner Skalierung eingenommen. Schauen Sie sich diese jsFiddle an, die Ihre erste nimmt und unten nur einen falschen Text hinzufügt. Beachten Sie, dass der Text darunter nicht nach oben verschoben wird, wenn die Feldhöhe auf Null skaliert wird.
Animuson
9
Jetzt ist es so: jsfiddle.net/gNDX3/1 Grundsätzlich müssen Sie Ihre Elemente nach Ihren Wünschen gestalten. In CSS / HTML gibt es kein Silver Bullet- oder Widget-ähnliches Verhalten.
dotnetCarpenter
70
Während ich jemandem applaudiere, der verschiedene Ansätze ausprobiert, ist der reale Effekt und die Komplikationen, die diese Lösung mit sich bringt, weitaus schlimmer als der ohnehin schon schreckliche Hack mit maximaler Höhe. Bitte nicht verwenden.
Mystrdat
2
@ SquareCat alles klar alles klar. Hier ist eine, die mehr Schublade wie: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter
202

Sie können derzeit keine Höhe animieren, wenn eine der beteiligten Höhen ist auto. Sie müssen zwei explizite Höhen festlegen.

Robertc
quelle
12
Es gibt normalerweise mehrere Möglichkeiten, ein Problem zu lösen, und nicht alle sind angemessen oder möglich. Ich weiß nicht, warum @JedidiahHurt und Ryan Ore versuchen, mögliche Antworten auf einer Q & A-Site einzuschränken. Besonders wenn man bedenkt, dass diese Antwort zuerst hier war. Doppelt so, da die akzeptierte Antwort eine Problemumgehung ist.
Hurra, ich helfe
5
Die Antwort von @jake ist weder elegant noch richtig. Die verknüpfte Antwort hier ist weitaus besser. Es funktioniert korrekt und behandelt alle Größenfälle - von der akzeptierten Antwort kann auch nichts gesagt werden.
GraphicsMuncher
5
Yup: es tutthis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy
4
Ist geplant, dies irgendwie zu beheben? Ich meine, das ist ziemlich natürlich zu erwarten, in der Lage zu sein, von 0 Höhe auf auto...
Augustin Riedinger
6
@Meliodas "nein / du kannst nicht" ist eine gültige und akzeptable Antwort auf Fragen zum Stapelüberlauf.
TylerH
122

Die Lösung , dass ich immer war zum ersten Ausblendung verwendet habe, dann schrumpfen die font-size, paddingund marginWerte. Es sieht nicht wie ein Wischtuch aus, funktioniert aber ohne statisches heightoder max-height.

Arbeitsbeispiel:

/* 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
quelle
Hat ordentlich für mich gearbeitet. Nicht ganz wie beim jQuery slideUp / Down. Sie können einen Unterschied nur bei schwachen CPUs wie alten Computern oder Mobiltelefonen feststellen und viele von ihnen gleichzeitig öffnen und schließen.
Cruz Nunez
3
Wenn Sie Schaltflächen oder andere Nicht-Text-Elemente haben, erhalten Sie unerwünschte Leerzeichen, ohne den Mauszeiger zu bewegen.
Dennis W
3
Sie können es weiter verfeinern, indem Sie alle untergeordneten Elemente mit auswählen *und andere Änderungen anwenden. Schaltflächen sollten jedoch von meinem obigen Code betroffen sein, wenn sie korrekt gestaltet wurden em.
Steven Vachon
1
In meinem Fall musste ich auch hinzufügen line-height: 0;undborder-width: 0;
Andrey Shatilov
1
Sie sollten keine Anpassungen line-heightvornehmen müssen, wenn Sie Einheiten für den Wert korrekt vermeiden: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon
93

Ich weiß, dass dies die dreißigste Antwort auf diese Frage ist, aber ich denke, es lohnt sich, also geht es weiter. Dies ist eine reine CSS- Lösung mit den folgenden Eigenschaften:

  • Am Anfang gibt es keine Verzögerung, und der Übergang wird nicht vorzeitig beendet. Wenn Sie in beiden Richtungen (Erweitern und Reduzieren) in Ihrem CSS eine Übergangsdauer von 300 ms angeben, dauert der Übergang 300 ms.
  • Es transform: scaleY(0)ändert die tatsächliche Höhe (im Gegensatz zu ), also macht es das Richtige, wenn nach dem zusammenklappbaren Element Inhalt vorhanden ist.
  • Während (wie in anderen Lösungen) gibt es magische Zahlen (wie „eine Länge auswählen , die höher ist als Ihre Box ist immer los zu sein“), es ist nicht tödlich , wenn Ihre Annahme endet falsch zu sein. Der Übergang sieht in diesem Fall vielleicht nicht besonders gut aus, aber vor und nach dem Übergang ist dies kein Problem: Im erweiterten ( height: auto) Status hat der gesamte Inhalt immer die richtige Höhe (im Gegensatz zum Beispiel, wenn Sie einen auswählen max-height, der sich als solche herausstellt zu niedrig). Und im zusammengeklappten Zustand ist die Höhe Null, wie sie sollte.

Demo

Hier ist eine Demo mit drei zusammenklappbaren Elementen unterschiedlicher Höhe, die alle dasselbe CSS verwenden. Möglicherweise möchten Sie auf "ganze Seite" klicken, nachdem Sie auf "Snippet ausführen" geklickt haben. Beachten Sie, dass das JavaScript nur die collapsedCSS-Klasse umschaltet , es ist keine Messung erforderlich. (Sie können genau diese Demo ohne JavaScript ausführen, indem Sie ein Kontrollkästchen verwenden oder :target). Beachten Sie auch, dass der Teil des CSS, der für den Übergang verantwortlich ist, ziemlich kurz ist und der HTML-Code nur ein einziges zusätzliches Wrapper-Element erfordert.

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

Wie funktioniert es?

Es gibt tatsächlich zwei Übergänge, die dazu beitragen, dass dies geschieht. Einer von ihnen wechselt margin-bottomvon 0px (im erweiterten Zustand) -2000pxin den reduzierten Zustand (ähnlich dieser Antwort ). Die 2000 hier ist die erste magische Zahl. Sie basiert auf der Annahme, dass Ihre Box nicht höher als diese ist (2000 Pixel scheinen eine vernünftige Wahl zu sein).

Die margin-bottomalleinige Verwendung des Übergangs hat zwei Probleme:

  • Wenn Sie tatsächlich eine Box haben, die höher als 2000 Pixel ist, margin-bottom: -2000pxwird a nicht alles verbergen - es wird auch im zusammengeklappten Fall sichtbares Material geben. Dies ist eine kleine Korrektur, die wir später vornehmen werden.
  • Wenn die tatsächliche Box beispielsweise 1000 Pixel hoch ist und Ihr Übergang 300 ms lang ist, ist der sichtbare Übergang bereits nach etwa 150 ms beendet (oder beginnt in der entgegengesetzten Richtung 150 ms zu spät).

Bei der Behebung dieses zweiten Problems kommt der zweite Übergang ins Spiel, und dieser Übergang zielt konzeptionell auf die Mindesthöhe des Wrappers ab ("konzeptionell", da wir die min-heightEigenschaft dafür nicht verwenden; dazu später mehr).

Hier ist eine Animation, die zeigt, wie die Kombination des Übergangs am unteren Rand mit dem Übergang der minimalen Höhe, beide von gleicher Dauer, einen kombinierten Übergang von voller Höhe zu Höhe von Null mit derselben Dauer ergibt.

Animation wie oben beschrieben

Der linke Balken zeigt, wie der negative untere Rand den Boden nach oben drückt und die sichtbare Höhe verringert. Der mittlere Balken zeigt, wie die Mindesthöhe sicherstellt, dass im kollabierenden Fall der Übergang nicht vorzeitig endet und im expandierenden Fall der Übergang nicht zu spät beginnt. Der rechte Balken zeigt, wie die Kombination der beiden bewirkt, dass die Box in der richtigen Zeit von voller Höhe auf null Höhe übergeht.

Für meine Demo habe ich mich für 50px als oberen Mindesthöhenwert entschieden. Dies ist die zweite magische Zahl, und sie sollte niedriger sein als die Höhe der Box jemals sein würde. 50px scheint ebenfalls vernünftig zu sein; Es ist unwahrscheinlich, dass Sie ein Element sehr oft zusammenklappbar machen möchten, das überhaupt nicht einmal 50 Pixel hoch ist.

Wie Sie in der Animation sehen können, ist der resultierende Übergang kontinuierlich, aber nicht differenzierbar. In dem Moment, in dem die minimale Höhe gleich der vollen Höhe ist, die durch den unteren Rand eingestellt wird, ändert sich die Geschwindigkeit plötzlich. Dies macht sich in der Animation sehr bemerkbar, da für beide Übergänge eine lineare Timing-Funktion verwendet wird und der gesamte Übergang sehr langsam ist. Im tatsächlichen Fall (meine Demo oben) dauert der Übergang nur 300 ms, und der Übergang am unteren Rand ist nicht linear. Ich habe mit vielen verschiedenen Timing-Funktionen für beide Übergänge herumgespielt, und diejenigen, mit denen ich endete, hatten das Gefühl, dass sie für die unterschiedlichsten Fälle am besten funktionieren.

Zwei Probleme müssen noch behoben werden:

  1. der Punkt von oben, an dem Felder mit einer Höhe von mehr als 2000 Pixel im reduzierten Zustand nicht vollständig ausgeblendet sind,
  2. und das umgekehrte Problem, bei dem im nicht ausgeblendeten Fall Felder mit einer Höhe von weniger als 50 Pixel zu hoch sind, selbst wenn der Übergang nicht ausgeführt wird, weil die minimale Höhe sie bei 50 Pixel hält.

Wir lösen das erste Problem, indem wir dem Containerelement max-height: 0im reduzierten Fall ein a mit einem 0s 0.3sÜbergang geben. Dies bedeutet, dass es sich nicht wirklich um einen Übergang handelt, der jedoch max-heightverzögert angewendet wird. Dies gilt nur, wenn der Übergang beendet ist. Damit dies korrekt funktioniert, müssen wir auch eine Zahl max-heightfür den entgegengesetzten, nicht reduzierten Zustand auswählen . Im Gegensatz zum Fall 2000px, bei dem die Auswahl einer zu großen Zahl die Qualität des Übergangs beeinflusst, spielt dies in diesem Fall keine Rolle. Wir können also einfach eine Zahl auswählen, die so hoch ist, dass wir wissen, dass keine Höhe jemals in die Nähe kommen wird. Ich habe eine Million Pixel ausgewählt. Wenn Sie der Meinung sind, dass Sie möglicherweise Inhalte mit einer Höhe von mehr als einer Million Pixel unterstützen müssen, dann 1) tut mir leid und 2) fügen Sie einfach ein paar Nullen hinzu.

Das zweite Problem ist der Grund, warum wir nicht min-heightfür den minimalen Höhenübergang verwenden. Stattdessen befindet sich ::afterim Container ein heightPseudoelement mit einem Übergang von 50px zu Null. Dies hat den gleichen Effekt wie min-height: Es lässt den Container nicht unter die Höhe schrumpfen, die das Pseudoelement derzeit hat. Aber weil wir heightnicht verwenden min-height, können wir jetzt max-height(erneut mit Verzögerung angewendet) die tatsächliche Höhe des Pseudoelements auf Null setzen, sobald der Übergang beendet ist, um sicherzustellen, dass zumindest außerhalb des Übergangs auch kleine Elemente die haben richtige Höhe. Weil min-heightes stärker ist als max-height, würde dies nicht funktionieren, wenn wir die Container min-heightanstelle der Pseudoelemente verwenden würdenheight. Genau wie max-heightim vorherigen Absatz max-heightbenötigt dies auch einen Wert für das andere Ende des Übergangs. Aber in diesem Fall können wir einfach die 50px auswählen.

Getestet in Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (mit Ausnahme eines Flexbox-Layout-Problems mit meiner Demo, das ich nicht debuggt habe) und Safari (Mac, iOS) ). Apropos Flexbox: Es sollte möglich sein, diese Funktion ohne Verwendung einer Flexbox auszuführen. Tatsächlich denke ich, dass Sie fast alles in IE7 zum Laufen bringen können - mit Ausnahme der Tatsache, dass Sie keine CSS-Übergänge haben, was es zu einer ziemlich sinnlosen Übung macht.

balpha
quelle
35
Ich wünschte, Sie könnten Ihre Lösung oder zumindest das Codebeispiel verkürzen (vereinfachen) - es sieht so aus, als wären 90% des Codebeispiels für Ihre Antwort nicht relevant.
Gil Epshtain
Gibt es eine Möglichkeit, diese Übergänge auch dann zum Funktionieren zu bringen, wenn die collapsible-wrapperPosition absolut ist? Das overflow: hiddenauf dem übergeordneten Element wird für den Übergang benötigt, stört jedoch absolut positionierte Elemente.
pmkro
1
@pmkro Ja, ich hatte auch dieses Problem. Ich habe es mit a clip-path: polygon(...)anstelle von gelöst overflow: hidden, da Sie im Gegensatz zu letzterem den ersteren wechseln können. Als Fallback für ältere Browser habe ich eine zusätzliche Skalierungstransformation verwendet. Siehe den Kommentar oben auf github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha
2
Nett. Das funktioniert wirklich gut. Sollte die akzeptierte Antwort sein
Henry Ing-Simmons
84

Sie können mit ein wenig nicht-semantischem Jiggery-Pokery. Mein üblicher Ansatz besteht darin, die Höhe eines äußeren DIV zu animieren, der ein einzelnes Kind hat. Dies ist ein stilloser DIV, der nur zum Messen der Inhaltshöhe verwendet wird.

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>

Man möchte einfach in der Lage sein, auf das zu verzichten .measuringWrapperund die Höhe des DIV auf Auto zu setzen und diese Animation zu haben, aber das scheint nicht zu funktionieren (die Höhe wird eingestellt, aber es erfolgt keine Animation).

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>

Meine Interpretation ist, dass eine explizite Höhe erforderlich ist, damit die Animation ausgeführt werden kann. Sie können keine Animation zur Höhe erhalten, wenn eine der Höhen (Start- oder Endhöhe) ist auto.

jhurshman
quelle
10
Da dies auf Javascript basiert, können Sie den MeasurementWrapper auch einfach mit Javascript hinzufügen!
Quickredfox
18
Sie können es ohne Wrapper tun. Nur: Funktion growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
user1742529
8
Schauen Sie sich das an ... sprechen Sie über eine einfache Antwort jsfiddle.net/n5XfG/2596 ... Ihre Antwort ist ohne guten Grund wahnsinnig komplex. Keine Notwendigkeit max-height, keine Notwendigkeit autound vor allem keine Notwendigkeit für Javascript !! Nur zwei einfache Erklärungen
VIDesignz
12
@VIDesignz Sie animieren über Marge: 100%. Dies ist 100% der Breite des Elements, nicht seiner Höhe! Das heißt, wenn Sie ein Element haben, dessen Höhe größer als seine Breite ist, wird es nicht ausgeblendet. Außerdem unterscheidet sich die tatsächliche Animationsgeschwindigkeit grundlegend von der definierten.
Timo Türschmann
3
Ich war begeistert von der Antwort von VIDesignz, bis ich mehr über Timos Kommentar las. Ja, margin-top(und margin-bottom) sind relativ zur Breite, wenn sie in Prozent definiert sind. Ja, das ist dumm, aber es erklärt auch, warum die Animationszeiten für das Öffnen und Schließen völlig unterschiedlich sind - es ist dasselbe, was Sie in der am besten bewerteten Antwort sehen, wenn Sie eine harte Antwort angeben -codierte maximale Höhe. Warum ist das so schwer richtig zu machen?
Coderer
52

Eine visuelle Problemumgehung zum Animieren der Höhe mithilfe von CSS3-Übergängen besteht darin, stattdessen das Auffüllen zu animieren.

Sie erhalten nicht den vollen Wipe-Effekt, aber wenn Sie mit der Übergangsdauer und den Füllwerten herumspielen, sollten Sie nah genug dran sein. Wenn Sie Höhe / maximale Höhe nicht explizit festlegen möchten, sollte dies das sein, wonach Sie suchen.

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 von stephbands über jsFiddle)

Katharsis
quelle
3
Ich denke, das ist die beste Antwort. Diese Technik kann sich auch auf die Anpassung der Polsterung der Kinder erstrecken. In meinem Fall konnte ich es mit expliziten Höhenübergängen für einige der offenbarten Inhalte kombinieren, und der Gesamteffekt ist viel erfolgreicher als die Umgehung der maximalen Höhe, die für die Enthüllung in Ordnung ist, aber einen unangenehmen Moment einleitet Verzögerung beim Verstecken. Ich würde eher überhaupt nicht animieren, als eine bedeutungslose Verzögerung einzuführen, die meine App nicht mehr reagieren lässt. Ich bin überrascht, dass so viele Leute denken, dass das akzeptabel ist.
Semikolon
17
Nur dass Sie hier die Höhe überhaupt nicht animieren. Sie animieren das Auffüllen ... es kann problemlos verschwinden, da es vom aktuellen Status bis auf 0 animiert werden kann. Wenn Sie jedoch genau beobachten, wann es erweitert wird, wird es mit dem Text geöffnet, und das Auffüllen wird nur animiert Ich weiß nicht, wie man von 0 auf Auto animiert ... es braucht einen numerischen Bereich ... so funktioniert Tweening.
Rob R
Dies ist eine gute Problemumgehung, die nicht hackig ist. Es ist nicht die beste Antwort auf diese Frage, aber es ist eine Alternative, die für mich funktioniert hat. Manchmal braucht man nur den Effekt einer Höhenanimation, nicht die vollständige Animation :)
Ivan Durst
Diese Lösung schlägt auch bei Verwendung einer Übergangsverzögerung fehl.
Michel Joanisse
Ich stimme auch zu, dass diese Lösung eine saubere Problemumgehung ist, die ziemlich flüssige Übergänge bietet, wenn Ihre Animation schnell genug ist (in meinem Fall ist sie für ein Akkordeon mit Übergängen von 0,1 s perfekt!). Es wird zwar nicht viele Anwendungen enthalten, aber die anderen werden diese Frage auch nicht beantworten!
Remi D
46

Meine Problemumgehung besteht darin, die maximale Höhe für eine schöne, glatte Animation auf die exakte Inhaltshöhe umzustellen und dann mithilfe eines Rückrufs für TransitionEnd die maximale Höhe auf 9999 Pixel festzulegen, damit die Größe des Inhalts frei geändert werden kann.

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
quelle
14
@ Derija93 Das verwendet einfache alte Javascript-Timeout-Animationen. Die Herausforderung besteht darin, stattdessen CSS3-Übergänge zu verwenden.
Adam
3
Nun ja. Aber warum sollten Sie stattdessen "CSS3-Übergänge" verwenden, wenn Sie in Ihrem Beispiel bereits jQuery verwenden, eine Bibliothek, die ohnehin viel Code für "weniger schreiben, mehr tun" bietet? Ich wollte darauf hinweisen, dass Ihre Version möglicherweise viel besser aussieht, auch wenn sie komplizierter ist, wenn sie NICHT jQuery verwendet, sodass sie auf praktisch jeder Website ausgeführt werden kann. Ich glaube, ich habe es stark falsch formuliert. Tut mir leid. ;)
Kiruse
27
@ Derija93 Vielleicht, weil CSS3-Übergänge (nach allen Quellen, die ich finden kann) eine viel bessere Leistung haben als jQuery-Animationen. (Eigentlich sehr wichtig für meinen aktuellen Anwendungsfall, der mich hierher gebracht hat.)
Mark Amery
4
@ Derija93 Weil Javascript-Animationen im Vergleich zu CSS3-Übergängen nur langsam ausgeführt werden, und bei jQuery-Animationen müssen Sie sich um die Animationsschleife kümmern. Animieren Sie etwas beim Klicken, klicken Sie dann schnell und beobachten Sie, wie sich Animationen wiederholen. Dies wird mit CSS3 behandelt.
AlbertEngelB
2
@ Dropped.on.Caprica Das ist jetzt ein bisschen alt. Ich habe bereits gesagt, dass ich das Konzept, das er zu demonstrieren versuchte, falsch verstanden habe. Bei einfachen Animationen ist dies .stopjedoch der Trick für das ziemlich komplexe Problem der Animationsschleife. Wenn Sie jetzt Höhe und Farbe animieren, aber nur die Höhenanimation unterbrechen möchten, werden die Dinge etwas schwieriger ... Ich verstehe, worum es geht.
Kiruse
43

Die akzeptierte Antwort funktioniert in den meisten Fällen, funktioniert jedoch nicht gut, wenn die divHöhe stark variieren kann. Die Animationsgeschwindigkeit hängt nicht von der tatsächlichen Höhe des Inhalts ab und kann abgehackt aussehen.

Sie können die eigentliche Animation weiterhin mit CSS ausführen, müssen jedoch JavaScript verwenden, um die Höhe der Elemente zu berechnen, anstatt zu versuchen, sie zu verwenden auto. Es ist keine jQuery erforderlich, obwohl Sie dies möglicherweise ein wenig ändern müssen, wenn Sie Kompatibilität wünschen (funktioniert in der neuesten Version von 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
quelle
Das mag hilfreich sein, aber ich kann es nicht sagen, weil ich nicht weiß, wie ich es ohne ein besseres Beispiel verwenden soll.
Ivan Durst
1
Sie übergeben einfach ein beliebiges DOM-Element (z. B. von document.getElementById('mydiv')oder $('#mydiv').get()), und die Funktion wechselt zwischen dem Ausblenden / Anzeigen. Wenn Sie CSS-Übergänge einrichten, wird das Element automatisch animiert.
Oleg Vaskevich
Ich habe einen Ausschnitt hinzugefügt. Dies hat den Vorteil, dass es für dynamisch dimensionierte Inhalte gut funktioniert.
Oleg Vaskevich
3
-1, da dies nicht "mit CSS" ist. Der Punkt der Frage ist für eine JS-freie Lösung. Ich denke, die "richtige" Antwort, das ist kein Hack, wäre dies jedoch ...
Dudewad
16
Downvote für die Verwendung von JS - ernsthaft? Die Hälfte der Antworten hier verwendet JS, sodass Ihre Ablehnung nicht fair oder notwendig erscheint. Außerdem verwendet diese Antwort immer noch CSS, um die eigentliche Animation auszuführen, die IMO der Punkt des OP war. Das einzige , was JS für verwendet wird , ist die tatsächliche Höhe zu berechnen, wie es ist unmöglich , so mit reinem CSS zu tun (und Einstellung min-heightwie in der erwarteten Antwort Probleme mit Animationsgeschwindigkeit verursacht , wenn die Größe der Liste kann stark variieren).
Oleg Vaskevich
30

Die Element.scrollHeightEigenschaft, die hier nützlich sein kann und dennoch mit einem reinen CSS-Übergang verwendet werden kann, wurde kaum erwähnt . Die Eigenschaft enthält immer die "volle" Höhe eines Elements, unabhängig davon, ob und wie sein Inhalt infolge der reduzierten Höhe (z height: 0. B. ) überläuft .

Als solches ist für ein height: 0(effektiv vollständig kollabiertes) Element seine "normale" oder "volle" Höhe durch seinen scrollHeightWert (ausnahmslos eine Pixellänge ) immer noch leicht verfügbar .

Angenommen, für ein solches Element ist der Übergang bereits wie z. B. eingerichtet (unter Verwendung der ulursprünglichen Frage):

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

Wir können die gewünschte animierte "Erweiterung" der Höhe nur mit CSS mit etwa der folgenden Funktion auslösen (hier wird angenommen, dass sich die ulVariable auf die Liste bezieht):

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

Das ist es. Wenn Sie die Liste reduzieren müssen, reicht eine der beiden folgenden Anweisungen aus:

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

Mein spezieller Anwendungsfall drehte sich um das Animieren von Listen unbekannter und oft beträchtlicher Länge. Daher war es mir unangenehm, mich auf eine willkürliche "groß genug" heightoder max-heightSpezifikation zu einigen und das Risiko von abgeschnittenen Inhalten oder Inhalten einzugehen, die Sie plötzlich scrollen müssen (wenn overflow: autozum Beispiel). . Darüber hinaus wird die Lockerung und das Timing bei max-heightLösungen auf Basis der Lösung unterbrochen , da die verwendete Höhe möglicherweise viel früher ihren Maximalwert erreicht, als dies max-heightzum Erreichen erforderlich wäre 9999px. Und wenn die Bildschirmauflösungen zunehmen, 9999pxhinterlassen Pixellängen einen schlechten Geschmack in meinem Mund. Diese spezielle Lösung löst das Problem meiner Meinung nach auf elegante Weise.

Schließlich hoffen wir, dass zukünftige Überarbeitungen von CSS die Notwendigkeit der Autoren erfüllen, diese Art von Dingen noch eleganter zu machen - überdenken Sie den Begriff "berechnet" gegenüber "verwendeten" und "aufgelösten" Werten und überlegen Sie, ob Übergänge für berechnete Werte gelten sollten Werte, einschließlich Übergänge mit widthund height(die derzeit eine besondere Behandlung erhalten).

amn
quelle
6
Sie haben es in den anderen Antworten hier nicht gefunden, da für die Interaktion mit dem DOM über die Element.scrollHeightEigenschaft JavaScript erforderlich ist und sie, wie in der Frage eindeutig angegeben, dies ohne JavaScript tun möchten .
TylerH
3
Es gibt viele andere Teile von CSS außer diesen beiden Pseudoklassen, die dieses Problem lösen können, wie die am höchsten bewerteten Antworten auf Seite 1 zeigen. Das Festlegen eines Stils in JavaScript ist kein "reines CSS", da JS an erster Stelle festgelegt werden muss. Wenn eine Person nach einer Nur-CSS-Lösung fragt, liegt dies häufig daran, dass sie kein JavaScript einfügen kann oder von ihrem aktuellen Projekt oder ihrer aktuellen Rolle nicht zugelassen wird.
TylerH
29

Verwendung max-heightmit unterschiedlicher Übergangserleichterung und Verzögerung für jeden Zustand.

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

Siehe Beispiel: http://jsfiddle.net/0hnjehjc/1/

Malihu
quelle
12
Das Problem hierbei ist, dass Sie lächerliche Max-Höhen wie verwenden müssen, um sicherzustellen, dass Sie in einer dynamischen Umgebung genügend Platz haben 999999px. Dies verschiebt andererseits Ihre Animation, da die Frames aus diesem Wert berechnet werden. Sie könnten also mit einer "Verzögerung" der Animation in eine Richtung und einem wirklich schnellen Ende in die andere Richtung enden (korr.: Timing-Funktionen)
Julian F. Weinert
29

Keine fest codierten Werte.

Kein JavaScript.

Keine Annäherungen.

Der Trick besteht darin, ein verstecktes und dupliziertes divzu verwenden, damit der Browser versteht, was 100% bedeutet.

Diese Methode eignet sich immer dann, wenn Sie das DOM des Elements, das Sie animieren möchten, duplizieren können.

.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
quelle
Gut gemacht - dies ist eine gültige Lösung für ein Problem, das eigentlich nicht existieren sollte. Das Animieren der maximalen Höhe ist bas, es sei denn, der Übergang ist auf "linear" eingestellt. Es ist auch (offensichtlich) sehr schlecht für das Animieren von CMS-Inhalten, bei denen die Höhe variabel ist.
M_Willett
Das ist klug, aber ich würde es nicht mögen, DOM-Elemente und -Inhalte dafür zu duplizieren.
Andre Figueiredo
Sie können dies ohne Container und leistungsfähiger tun, indem Sie animieren transform: scaleY(1), da durch diesen Übergang kein Leerzeichen entfernt wird.
Fabian von Ellerts
21

Während ich dies poste, gibt es bereits über 30 Antworten, aber ich habe das Gefühl, dass sich meine Antwort gegenüber der bereits akzeptierten Antwort von Jake verbessert .

Ich war nicht zufrieden mit dem Problem, das sich aus der einfachen Verwendung von max-heightCSS3-Übergängen ergibt , da Sie, wie viele Kommentatoren feststellten, Ihren max-heightWert sehr nahe an der tatsächlichen Höhe einstellen müssen, da sonst eine Verzögerung auftritt. In dieser JSFiddle finden Sie ein Beispiel für dieses Problem.

Um dies zu umgehen (ohne JavaScript zu verwenden), habe ich ein weiteres HTML-Element hinzugefügt, das den transform: translateYCSS-Wert überführt.

Dies bedeutet beides max-heightund translateYwird verwendet: max-heightErmöglicht dem Element, Elemente darunter nach unten zu drücken, während translateYder gewünschte "Sofort" -Effekt erzielt wird. Das Problem mit max-heightbesteht immer noch, aber seine Wirkung wird verringert. Dies bedeutet, dass Sie eine viel größere Höhe für Ihren max-heightWert einstellen und sich weniger Sorgen machen können.

Der Gesamtvorteil besteht darin, dass der Benutzer beim Übergang zurück in (dem Zusammenbruch) die translateYAnimation sofort sieht , sodass es nicht wirklich darauf ankommt, wie lange dies max-heightdauert.

Lösung als Geige

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
quelle
Schöner Vergleich @davidtaubmann, Ihr Stift zeigt die Unterschiede sehr gut! Ich ging mit translateYüber, scaleweil ich nicht mochte, wie scaledieser Komprimierungseffekt gab.
Andrew Messier
Thanks @ Andrew-unordentliche, löschte ich den Kommentar , weil es ein Fehler ist, translateY ist excecuting nicht in codepen.io/davidtaubmann/pen/zKZvGP (weil es kein Kind ist die Bewegung zu tun, muss ein Update) ... aber in diesem Zum einen können wir den Vergleich von ONLY MAX-HEIGHT perfekt sehen und mit der Skala mischen (wie in anderen Antworten angegeben): codepen.io/davidtaubmann/pen/jrdPER . Dies alles hat mir sehr geholfen mit einer
Zielmethode, die
18

Ok, ich denke, ich habe eine super einfache Antwort gefunden ... nein max-height, verwendet relativePositionierung, arbeitet an liElementen und ist reines CSS. Ich habe in nichts anderem als Firefox getestet, obwohl es nach CSS nach allen Browsern funktionieren sollte.

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
quelle
13
Dies funktioniert so lange, bis die Höhe des Elements seine Breite überschreitet. Dies ist die Grundlage für die Berechnung der Ränder anhand von Prozentsätzen: Sie werden basierend auf der Breite des Elements berechnet. Wenn Sie also ein 1000 px breites Element haben, ist ein Element mit 1100 px zu groß, als dass diese Lösung funktionieren könnte, was bedeutet, dass Sie diesen negativen oberen Rand erhöhen müssten. Grundsätzlich ist es genau das gleiche Problem wie bei der Verwendung von Höhe oder maximaler Höhe.
Dudewad
15

BEARBEITEN: Scrollen Sie nach unten, um eine aktualisierte Antwort zu erhalten.
Ich habe eine Dropdown-Liste erstellt und diesen Beitrag gesehen ... viele verschiedene Antworten, aber ich entscheide mich, meine Dropdown-Liste auch zu teilen. Es ist nicht perfekt, aber zumindest wird nur CSS für verwendet Dropdown-Liste! Ich habe transform: translateY (y) verwendet, um die Liste in die Ansicht zu transformieren ...
Sie können mehr im Test sehen
http://jsfiddle.net/BVEpc/4/
Ich habe div hinter jedes li gesetzt, weil mein Die Dropdown-Liste kommt von oben und um ihnen richtig zu zeigen, dass dies erforderlich war, lautet mein Div-Code:

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

und schweben ist:

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

und weil die ul-höhe auf den inhalt eingestellt ist, kann es über deinen körperinhalt kommen, deshalb habe ich das für ul gemacht:

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

und schwebe:

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

Das zweite Mal nach dem Übergang ist eine Verzögerung und es wird ausgeblendet, nachdem meine Dropdown-Liste ungefähr geschlossen wurde ... Ich
hoffe, dass später jemand davon profitiert.

EDIT: Ich kann einfach nicht glauben, dass ppl diesen Prototyp tatsächlich benutzt! Dieses Dropdown-Menü ist nur für ein Untermenü und das ist alles !! Ich habe ein besseres aktualisiert, das zwei Untermenüs für ltr- und rtl-Richtung mit IE 8-Unterstützung haben kann.
Fiddle for LTR
Fiddle for RTL
hoffentlich findet jemand dies in Zukunft nützlich.

Sijav
quelle
11

Sie können von Höhe: 0 zu Höhe: Automatisch wechseln, sofern Sie auch die Mindest- und Höchsthöhe angeben.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
Stuart Badminton
quelle
@Stuart Badminton können Sie ein funktionierendes Beispiel liefern? Ich habe dies zusammengestellt, aber es scheint nirgendwo zu funktionieren :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin
5
Das Problem liegt in Ihrer Übergangsregel. Damit es funktioniert, müssen Sie den Übergang auch auf die minimale und maximale Höhe anwenden. Übergang: Höhe .5s linear, min-Höhe .5s linear, max-Höhe .5s linear
Stuart Badminton
2
Hier ist, was ich später bekommen habe, genau wie du gesagt hast, um die maximale Höhe zu animieren: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin
11
Natürlich gibt es ein Problem damit. Die Zeit, die zum Animieren der maximalen Höhe benötigt wird, ist nicht die Zeit, die zum Übergang zur automatischen Höhe der Box benötigt wird: Sie erreicht die Höhe: zuerst automatisch, bevor die maximale Höhe die Animation beendet hat. In ähnlicher Weise beginnt der Übergang beim Übergang auf 0 nicht sofort, da die maximale Höhe bei einer Höhe beginnt, die größer als die Höhe ist: auto. Der Übergang zur maximalen Höhe: 1000px macht dies deutlich: jsfiddle.net/n5XfG/11
stephband
Sie haben Recht, bei weiteren Tests habe ich dies bemerkt. Ich weiß nicht, ob jemals eine volle Höhe von 0 bis Höhe: Auto implementiert wird, aber die Verwendung einer maximalen Höhe in einigen Situationen löst eine ansonsten unmögliche Situation, wenn nur CSS verwendet wird.
Stuart Badminton
10

Flexbox-Lösung

Vorteile:

  • einfach
  • nein JS
  • weicher Übergang

Nachteile:

  • Das Element muss in einen Flexbehälter mit fester Höhe gegeben werden

Die Art und Weise, wie es funktioniert, besteht darin, immer eine Flex-Basis zu haben: Auto auf dem Element mit Inhalt und stattdessen Flex-Grow und Flex-Shrink.

Bearbeiten: Verbesserte JS-Geige, inspiriert von der Xbox One-Oberfläche.

* {
  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
quelle
7

Wenn Sie die Antwort von @ jake erweitern, wird der Übergang bis zum maximalen Höhenwert ausgeführt, was zu einer extrem schnellen Animation führt. Wenn Sie die Übergänge für beide festlegen: Schweben und Ausschalten, können Sie die verrückte Geschwindigkeit ein wenig mehr steuern.

Der Li: Hover ist also, wenn die Maus in den Status wechselt und der Übergang für die nicht schwebende Eigenschaft der Mausausgang ist.

Hoffentlich hilft das etwas.

z.B:

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

Hier ist eine Geige: http://jsfiddle.net/BukwJ/

Jamie
quelle
1
Ich weiß nicht, warum das eine -1 hat. Ich denke tatsächlich, dass dies ein besserer Versuch ist als einige der Leute, die eine Menge Javascript hinzufügen. Es ist keine perfekte Lösung, aber mit einigen Optimierungen macht es einen guten Job. Ich fand, dass dies für den Rückgabewert funktioniert. Übergang: alle 500 ms Kubikbeier (0,000, 1,225, 0,085, 0,385); Dies basierte auf einem 2s-Übergang beim Öffnen und dann dem obigen Code, um zum Ausgangszustand zurückzukehren. Danke ... Das hat mir besser geholfen als alle oben genannten. +1 Ich habe diese Seite benutzt, um die bezier matthewlein.com/ceaser
gcoulby
OK, es dauerte ein bisschen mehr Optimierungen. Übergang: alle 500 ms Kubikbeier (0,000, 1,390, 0,000, 0,635); Ich sollte angeben, dass mein Übergang von 5% bis 100% reicht (in Wirklichkeit beträgt der Endwert jedoch etwa 28%), sodass dies vom Projekt abhängt.
Gcoulby
Absolut, es ist nicht ideal, aber es ist eine schnelle Lösung, wenn Sie ungefähr wissen, wie hoch die durchschnittliche Höhe sein wird.
Jamie
7

Ich denke, ich habe eine wirklich solide Lösung gefunden

OK! Ich weiß, dass dieses Problem so alt ist wie das Internet, aber ich glaube, ich habe eine Lösung, die ich in ein Plugin namens Mutant-Transition verwandelt habe . Meine Lösung legt die style=""Attribute für verfolgte Elemente fest, wenn sich das DOM ändert. Das Endergebnis ist, dass Sie gutes altes CSS für Ihre Übergänge verwenden können und keine hackigen Fixes oder spezielles Javascript verwenden. Das einzige, was Sie tun müssen, ist festzulegen, was Sie mit dem betreffenden Element verfolgen möchten data-mutant-attributes="X".

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

Das ist es! Diese Lösung verwendet MutationObserver , um Änderungen im DOM zu verfolgen. Aus diesem Grund müssen Sie nichts einrichten oder Javascript verwenden, um Dinge manuell zu animieren. Änderungen werden automatisch nachverfolgt. Da jedoch MutationObserver verwendet wird, wird dies nur in IE11 + übergehen.

Geigen!

JonTroncoso
quelle
12
Wenn jemand fragt, wie etwas "ohne JavaScript" ausgeführt werden soll, bedeutet dies normalerweise, dass die Lösung in Browsern funktionieren muss, in denen JavaScript deaktiviert ist. Es bedeutet nicht "ohne meine eigenen Skripte zu schreiben". Zu sagen, dass Ihr Plugin ohne Verwendung von JavaScript verwendet werden kann, ist bestenfalls irreführend - wenn man bedenkt, dass es sich um ein JavaScript-Plugin handelt.
BoltClock
Ich sollte klarstellen: Wenn ich sage, dass Sie kein spezielles Javascript verwenden müssen, meine ich, dass Sie kein Javascript schreiben müssen . Fügen Sie einfach die JS-Bibliothek hinzu und geben Sie an, welche Attribute Sie im HTML-Code anzeigen möchten. Sie müssen kein CSS mit fester Höhe verwenden oder etwas herausfinden. Einfach stylen und loslegen.
JonTroncoso
"(...) Sie müssen nicht wirklich etwas einrichten oder Javascript verwenden, um Dinge manuell zu animieren (...)", also verwenden Sie JavaScript zum Spaß
Roko C. Buljan
6

Hier ist eine Möglichkeit, von einer beliebigen Starthöhe, einschließlich 0, zu einer automatischen (voller Größe und flexibel) überzugehen, ohne dass ein fest festgelegter Code pro Knoten oder ein Benutzercode zum Initialisieren erforderlich ist: https://github.com/csuwildcat / Transition-Auto . Dies ist im Grunde der heilige Gral für das, was Sie wollen, glaube ich -> http://codepen.io/csuwldcat/pen/kwsdF . Fügen Sie einfach die folgende JS-Datei in Ihre Seite ein, und danach müssen Sie nur noch ein einzelnes boolesches Attribut hinzufügen / entfernen - reveal=""- von den Knoten, die Sie erweitern und verkleinern möchten.

Hier ist alles, was Sie als Benutzer tun müssen, sobald Sie den Codeblock unter dem Beispielcode eingefügt haben:

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

Hier ist der Codeblock, der in Ihre Seite aufgenommen werden soll. Danach ist alles Sauce:

Legen Sie diese JS-Datei auf Ihrer Seite ab - alles funktioniert

/ * Code für Höhe: auto; Übergang * /

(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 für 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
quelle
9
Dies ist zwar eine viel sauberere und flexiblere Lösung als die anderen vorgeschlagenen, aber ich persönlich glaube, dass dies nicht etwas sein sollte, das JS überhaupt berühren sollte. Nicht zu sagen, dass du falsch liegst, es ist nur ein Seufzer.
Mystrdat
@mystrdat Um klar zu sein, wird JS nur verwendet, um nach bestimmten Bedingungen zu suchen und ein paar Attribute / temporäre CSS-Werte hinzuzufügen, basierend auf den Ereignissen / Bedingungen, nach denen es sucht. Der Übergang erfolgt noch vollständig in CSS. Obwohl ich damit einverstanden bin, ist es traurig, dass dieses Maß an Einrichtung und Handhaltung notwendig ist, um einen scheinbar einfachen Anwendungsfall wirklich zu befriedigen! : /
csuwldcat
1
@ KarolisRamanauskas Nein, das ist kein Problem. Der IE unterstützt sowohl CSS-Keyframes als auch requestAnimationFrame. Ich habe clipals Dummy-Eigenschaftsauslöser verwendet, und aus irgendeinem Grund hat der IE die Animation von Clips eingestellt. Ich habe auf Deckkraft umgestellt und alles funktioniert wieder: codepen.io/csuwldcat/pen/FpzGa . Ich habe den Code in der Antwort entsprechend aktualisiert.
Csuwldcat
1
@csuwldcat Sehr schöne Lösung, ich habe festgestellt, dass der Inhalt für einen kleinen Bruchteil erscheint und verschwindet. Warum passiert das? Ist es vermeidbar?
Beto Aveiga
12
Ich möchte dies positiv bewerten, aber anstatt die Frage zu beantworten, wie es funktioniert, haben Sie ein Plugin freigegeben, das Sie geschrieben haben, damit es funktioniert. Wir, die Neugierigen, müssen Ihr Plugin zurückentwickeln, was nicht viel Spaß macht. Ich wünschte, Sie würden Ihre Antwort aktualisieren, um mehr Erklärungen darüber zu erhalten, was Ihr Plugin tut und warum. Ändern Sie den Code, um eine Erklärung zu erhalten. Zum Beispiel haben Sie einen ganzen Codeabschnitt, der nur statisches CSS schreibt. Ich würde lieber das CSS sehen als den Code, der es generiert. Sie können die langweiligen Teile weglassen, z. B. das Wiederholen für alle Browserpräfixe.
gilly3
6

Jakes Antwort, die maximale Höhe zu animieren, ist großartig, aber ich fand die Verzögerung, die durch das Einstellen einer großen maximalen Höhe verursacht wurde, ärgerlich.

Man könnte den zusammenklappbaren Inhalt in ein inneres Div verschieben und die maximale Höhe berechnen, indem man die Höhe des inneren Div ermittelt (über JQuery wäre es das OuterHeight ()).

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

Hier ist ein jsfiddle-Link: http://jsfiddle.net/pbatey/duZpT

Hier ist eine jsfiddle mit der absolut minimalen Menge an Code, die erforderlich ist: http://jsfiddle.net/8ncjjxh8/

pbatey
quelle
5

Mir ist klar, dass dieser Thread in die Jahre gekommen ist, aber bei bestimmten Google-Suchanfragen einen hohen Stellenwert hat. Ich denke, es lohnt sich, ihn zu aktualisieren.

Sie erhalten / stellen auch einfach die eigene Höhe des Elements ein:

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

Sie sollten dieses Javascript unmittelbar nach dem schließenden Tag von target_box in einem Inline-Skript-Tag sichern.

Charley
quelle
document.getElementById ('target_box'). getBoundingClientRect (). height sollte am besten funktionieren.
Ändern Sie den
5

Ich konnte das tun. Ich habe ein .child& ein .parentdiv. Das Kinder-Div passt mit der absolutePositionierung perfekt in die Breite / Höhe des Elternteils . Ich animiere dann die translateEigenschaft, um ihren YWert nach unten zu drücken 100%. Seine sehr flüssige Animation, keine Störungen oder Nachteile wie bei jeder anderen Lösung hier.

So etwas wie Pseudocode

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

Sie würden angeben , eine heightauf .parent, in px, %oder Urlaub , wie auto. Dieses Div maskiert dann das .childDiv, wenn es nach unten rutscht.

Josh Ribakoff
quelle
Das Arbeitsbeispiel dafür wird sehr geschätzt.
Neurotransmitter
5

Ich habe eine Antwort mit etwas JavaScript gepostet und wurde herabgestimmt, war also verärgert und habe es erneut versucht und habe sie nur mit CSS geknackt!

Diese Lösung verwendet einige "Techniken":

  • padding-bottom:100%'Hack', bei dem Prozentsätze in Bezug auf die aktuelle Breite des Elements definiert werden. Weitere Infos zu dieser Technik.
  • Float Shrink-Wrapping (erfordert ein zusätzliches Div, um den Float Clearing Hack anzuwenden)
  • unsemantische Verwendung von https://caniuse.com/#feat=css-writing-mode und einige Transformationen zum Rückgängigmachen (dies ermöglicht die Verwendung des oben genannten Padding-Hacks in einem vertikalen Kontext)

Das Ergebnis ist jedoch, dass wir einen performanten Übergang nur mit CSS und eine einzige Übergangsfunktion erhalten, um den Übergang reibungslos zu erreichen. Der Heilige Gral!

Natürlich gibt es einen Nachteil! Ich kann nicht herausfinden, wie die Breite gesteuert werden soll, bei der Inhalte abgeschnitten werden ( overflow:hidden). Aufgrund des Polster-Boden-Hacks sind Breite und Höhe eng miteinander verbunden. Es kann aber einen Weg geben, also werde ich darauf zurückkommen.

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
quelle
Der andere Nachteil ist, dass Sie den Cursor nicht vom "Hoverme" -Div auf die Liste der Elemente bewegen können, um mit ihnen zu interagieren (angeblich der Grund für OP-Fragen). Daher ist er nur als Tooltip-Funktion nützlich und nicht Zum Beispiel ein Menü mit verschachtelten Untermenüs.
TylerH
Dies ist lediglich eine Folge der Art und Weise, wie die Frage formuliert wurde, und hängt nicht mit der Technik zusammen. Sie können den Auslöser so ändern, dass er ein ist, input[type="checkbox"]:checkedanstatt, :hoverwenn Sie möchten (dh Sie möchten kein JavaScript verwenden, um Klassen hinzuzufügen)
EoghanM
Ich meine, unabhängig davon, wie die Frage formuliert ist, sollte eine Implementierung einer Lösung, wenn sie so begrenzt ist, wahrscheinlich in der Antwort dieser Lösung erwähnt werden (oder vorzugsweise berücksichtigt werden ). Sie erwähnen, dass diese Lösung ein "heiliger Gral" ist, aber der übergegangene Div kann nicht einmal darüber schweben ... scheint mir keine so ultimative Lösung zu sein.
TylerH
Hallo @ TylerH, entschuldigung, ich habe die Tatsache übersehen, dass der Auslöser in der ursprünglichen Frage die erweiterte Liste umgibt, sodass die Liste offen bleiben sollte, wenn Sie den Mauszeiger darüber halten. Ich habe meine Antwort aktualisiert, um dies widerzuspiegeln. Wie ich bereits erwähnt habe, ist die Auslösung nicht allzu wichtig, da ich eine neue Technik vorstelle, die bisher nur in CSS nicht für möglich gehalten wurde (ich habe seit einigen Jahren nach einer Lösung gesucht).
EoghanM
4

Hier ist eine Lösung, die ich gerade in Kombination mit jQuery verwendet habe. Dies funktioniert für die folgende HTML-Struktur:

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

und die Funktion:

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

Sie können dann eine Klickfunktion verwenden, um die Höhe mithilfe von festzulegen und zu entfernen 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
quelle
Dies zeigt sich nicht richtig mit der neuesten jquery-Version
oliverbachmann
4

Ich habe kürzlich eher max-heightdie liElemente als die Umhüllung geändert ul.

Der Grund dafür ist, dass die Verzögerung für kleine max-heights(wenn überhaupt) im Vergleich zu großen weitaus weniger spürbar ist max-heights, und ich kann meinen max-heightWert auch relativ zu der font-sizevon liund nicht von einer beliebigen großen Zahl festlegen, indem ich emsoder verwende rems.

Wenn meine Schriftgröße ist 1rem, setze ich meine max-heightauf etwas wie 3rem(um umbrochenen Text aufzunehmen). Sie können hier ein Beispiel sehen:

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

Achtsamkeit
quelle
3

Ich habe nicht alles im Detail gelesen, aber ich hatte kürzlich dieses Problem und habe Folgendes getan:

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

Auf diese Weise können Sie ein Div erstellen, das zunächst Inhalte mit einer Höhe von bis zu 200 Pixel anzeigt und beim Schweben mindestens so groß ist wie der gesamte Inhalt des Div. Die Div wird nicht 3000px, aber 3000px ist die Grenze, die ich auferlege. Stellen Sie sicher, dass der Übergang auf non: hover erfolgt, da Sie sonst möglicherweise ein seltsames Rendering erhalten. Auf diese Weise erbt der: hover vom non: hover.

Der Übergang von px zu% oder zu auto funktioniert nicht. Sie müssen dieselbe Maßeinheit verwenden. Das funktioniert gut für mich. Die Verwendung von HTML5 macht es perfekt ....

Denken Sie daran, dass es immer eine Arbeit gibt ...; )

Hoffe, jemand findet das nützlich

work.stella84
quelle
3

Die Max-Height-Lösung von Jake funktioniert gut, wenn der angegebene hartcodierte Max-Height-Wert nicht viel größer als die tatsächliche Höhe ist (da sonst unerwünschte Verzögerungen und Zeitprobleme auftreten). Wenn andererseits der fest codierte Wert versehentlich nicht größer als die tatsächliche Höhe ist, öffnet sich das Element nicht vollständig.

Die folgende Nur-CSS-Lösung erfordert auch eine fest codierte Größe, die größer sein sollte als die meisten der auftretenden realen Größen. Diese Lösung funktioniert jedoch auch, wenn die tatsächliche Größe in einigen Situationen größer als die fest codierte Größe ist. In diesem Fall springt der Übergang möglicherweise etwas, hinterlässt jedoch niemals ein teilweise sichtbares Element. Diese Lösung kann also auch für unbekannte Inhalte verwendet werden, z. B. aus einer Datenbank, in der Sie nur wissen, dass der Inhalt normalerweise nicht größer als x Pixel ist, es gibt jedoch Ausnahmen.

Die Idee ist, einen negativen Wert für den unteren Rand (oder den oberen Rand für eine etwas andere Animation) zu verwenden und das Inhaltselement in ein mittleres Element mit Überlauf zu platzieren: versteckt. Der negative Rand des Inhaltselements verringert somit die Höhe des mittleren Elements.

Der folgende Code verwendet einen Übergang am unteren Rand von -150px zu 0px. Dies allein funktioniert einwandfrei, solange das Inhaltselement nicht höher als 150 Pixel ist. Außerdem wird für das mittlere Element ein Übergang der maximalen Höhe von 0px zu 100% verwendet. Dadurch wird das mittlere Element schließlich ausgeblendet, wenn das Inhaltselement höher als 150 Pixel ist. Bei maximaler Höhe wird der Übergang nur verwendet, um seine Anwendung beim Schließen um eine Sekunde zu verzögern, nicht für einen glatten visuellen Effekt (und kann daher von 0px bis 100% laufen).

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>

Der Wert für den unteren Rand sollte negativ sein und so nahe wie möglich an der tatsächlichen Höhe des Inhaltselements liegen. Wenn es (der Absoute-Wert) größer ist, gibt es ähnliche Verzögerungs- und Zeitprobleme wie bei den Lösungen mit maximaler Höhe, die jedoch begrenzt werden können, solange die fest codierte Größe nicht viel größer als die tatsächliche ist. Wenn der Absolutwert für Rand-Boden kleiner als die tatsächliche Höhe ist, springt die Tansition etwas. In jedem Fall wird das Inhaltselement nach dem Übergang entweder vollständig angezeigt oder vollständig entfernt.

Weitere Informationen finden Sie in meinem Blogbeitrag unter http://www.taccgl.org/blog/css_transition_display.html#combined_height

Helmut Emmelmann
quelle
3

Sie können dies tun, indem Sie eine umgekehrte (Reduzierungs-) Animation mit Clip-Pfad erstellen.

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

Dies ist nicht gerade eine "Lösung" für das Problem, sondern eher eine Problemumgehung. Es funktioniert nur so, wie es mit Text geschrieben wurde, kann aber bei Bedarf geändert werden, um mit anderen Elementen zu arbeiten, da bin ich mir sicher.

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

Hier ist ein Beispiel: http://codepen.io/overthemike/pen/wzjRKa

Im Wesentlichen setzen Sie die Schriftgröße auf 0 und wechseln diese anstelle der Höhe oder der maximalen Höhe oder der SkalierungY () usw. schnell genug, um die Höhe so zu gestalten, wie Sie es möchten. Das Umwandeln der tatsächlichen Höhe mit CSS in Auto ist derzeit nicht möglich, das Umwandeln des Inhalts jedoch ist möglich, daher der Übergang der Schriftgröße.

  • Hinweis - Es gibt Javascript im Codepen, aber der einzige Zweck besteht darin, CSS-Klassen beim Klicken für das Akkordeon hinzuzufügen / zu entfernen. Dies kann mit versteckten Optionsfeldern geschehen, aber ich habe mich nicht darauf konzentriert, sondern nur auf die Höhenumwandlung.
Mike S.
quelle
1
Dupliziert diese Antwort effektiv , lässt jedoch den Opazitäts-Fade-Effekt aus.
Selten
Einverstanden. Besserer Ansatz.
Mike S.