Jak mogę zmienić wysokość: 0; do wysokości: auto; używasz CSS?

2132

Usiłuję <ul>zjechać w dół za pomocą przejść CSS.

<ul>Zaczyna się height: 0;. Po najechaniu kursorem wysokość jest ustawiona na height:auto;. Jednak powoduje to, że po prostu pojawia się, a nie przejście,

Jeśli zrobię to od height: 40px;do height: auto;, wtedy przesunie się w górę height: 0;, a następnie nagle skoczy na odpowiednią wysokość.

Jak inaczej mogę to zrobić bez użycia JavaScript?

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

Hailwood
źródło
Uważam, że height:auto/max-heightrozwiązanie będzie działać tylko wtedy, gdy obszar powiększania jest większy niż heightchcesz ograniczyć. Jeśli masz max-heightod 300px, ale rozwijana pola kombi, które mogą wrócić 50px, to max-heightnie pomoże, 50pxjest zmienna w zależności od liczby elementów, można dotrzeć do niemożliwej sytuacji, gdy nie można ustalić go, ponieważ heightnie jest naprawiono, height:autobyło rozwiązaniem, ale nie mogę z tym korzystać z przejść.
Christopher Thomas
51
PO próbuje do sporządzania roztworu css nie js, w przeciwnym razie mogą one po prostu użyć przepełnienia i animacji
Toni Leigh
5
@VIDesignz: Ale -100% margines wewnętrzny div otrzymuje szerokość otoki div, a nie wysokość. To rozwiązanie ma ten sam problem, co rozwiązanie o maksymalnej wysokości. Ponadto, gdy szerokość jest mniejsza niż wysokość treści, nie cała treść jest ukryta o -100% marginesu. To jest złe rozwiązanie.
GregTom,
1
Zobacz github.com/w3c/csswg-drafts/issues/626, aby omówić specyfikację i wdrożenie właściwego rozwiązania
Ben Creasy

Odpowiedzi:

2744

Użyj max-heightw przejściu i nie height. I ustaw wartość na max-heightcoś większego niż twoje pudełko kiedykolwiek dostanie.

Zobacz demo JSFiddle dostarczone przez Chrisa Jordana w innej odpowiedzi tutaj.

#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
źródło
323
to działa świetnie! z wyjątkiem tego, że występuje opóźnienie, gdy zaczyna się, ponieważ zaczyna się dla maksymalnej wysokości, która początkowo jest bardzo wysoka .. hmm, myślę, że to jest trochę denerwujące
vsync
175
+1 Świetne rozwiązanie! Szybkość przejścia jest obliczana jako czas określony dla przejścia do wartości maksymalnej wysokości ... ale ponieważ wysokość będzie mniejsza niż maksymalna wysokość, przejście do rzeczywistej wysokości nastąpi szybciej (często znacznie) niż określony czas.
kingjeffrey,
50
Należy pamiętać, że może to powodować brzydkie zakończenie przejścia, gdy trzeba użyć wartości znacznie większych niż rzeczywista wartość obliczona. Zauważyłem to, starając się, aby div zwiększył się z wysokości 0 do wysokości treści, która różni się znacznie ze względu na różne rozmiary ekranu (2 linie na moim monitorze 2560 x 1440 vs.> 10 linii na smartfonie). W tym celu skończyłem z js.
jpeltoniemi
44
Bardzo brzydkie rozwiązanie, ponieważ powoduje opóźnienie w jednym kierunku, ale nie w drugim.
Tim
84
To dość leniwe rozwiązanie. Zakładam, że OP chce użyć wysokości: auto, ponieważ zwiększona wysokość kontenera jest nieco nieprzewidywalna. To rozwiązanie spowoduje opóźnienie, zanim animacja stanie się widoczna. Dodatkowo widoczny czas trwania animacji będzie nieprzewidywalny. Otrzymasz znacznie bardziej przewidywalne (i prawdopodobnie bardziej płynne) wyniki, obliczając łączną wysokość każdego z potomnych węzłów kontenerów, a następnie łagodząc do dokładnej wartości wysokości.
Shawn Whinnery
314

Zamiast tego powinieneś użyć skaliY.

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>

Stworzyłem wersję jsfiddle z prefiksem dostawcy i zmieniłem twój jsfiddle, aby używał skaliY zamiast wysokości.

dotnetCarpenter
źródło
6
Poprawiam tę odpowiedź, ponieważ działa ona dobrze w przeglądarkach obsługujących przejścia css3, ale należy zauważyć, że w IE <9 to nie będzie działać i nie będzie animowane (po prostu przeskoczy) w IE <10
Hailwood
215
Ta metoda tylko częściowo osiąga pożądany efekt, ale tak naprawdę nie usuwa przestrzeni. Przekształcone pudełko działa jak względnie pozycjonowany element - przestrzeń jest zajmowana bez względu na to, jak jest skalowana. Sprawdź ten jsFiddle, który bierze swój pierwszy i po prostu dodaje fałszywy tekst na dole. Zwróć uwagę, że poniższy tekst nie przesuwa się w górę, gdy wysokość ramki jest skalowana do zera.
animuson
9
Teraz robi to: jsfiddle.net/gNDX3/1 Zasadniczo musisz stylizować elementy zgodnie z tym, czego potrzebujesz. W CSS / HTML nie ma takiego zachowania jak srebrna kula lub widżet.
dotnetCarpenter
70
Oklaskuję komuś, kto próbuje różnych podejść, ale rzeczywisty efekt i komplikacje, jakie przynosi to rozwiązanie, są znacznie gorsze niż i tak już okropny hack na maksymalnej wysokości. Proszę nie używać
mystrdat
2
@SquareCat w porządku w porządku. Oto jedna, która jest bardziej szufladowa: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter
202

Obecnie nie można animować wysokości, gdy jedną z zaangażowanych wysokości jest auto, musisz ustawić dwie wyraźne wysokości.

robertc
źródło
12
Zwykle istnieje wiele sposobów rozwiązania problemu i nie wszystkie są odpowiednie lub możliwe. Nie wiem, dlaczego @JedidiahHurt i Ryan Ore starają się ograniczyć możliwe odpowiedzi na stronie pytań i odpowiedzi. Zwłaszcza biorąc pod uwagę, że ta odpowiedź była tutaj pierwsza. Podwójnie, ponieważ zaakceptowana odpowiedź jest obejściem.
Hooray Im Helping
5
Odpowiedź od @jake nie jest ani elegancka, ani poprawna. Połączona odpowiedź tutaj jest znacznie lepsza. Działa poprawnie i obsługuje skrzynki wszystkich rozmiarów - nie można powiedzieć o zaakceptowanej odpowiedzi.
GraphicsMuncher,
5
Tak: takthis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy
4
Czy planuje się to jakoś naprawić? Mam na myśli, że jest całkiem naturalne, że można przejść z wysokości 0 na auto...
Augustin Riedinger
6
@Meliodas „nie / nie możesz” jest prawidłową i akceptowalną odpowiedzią na pytania dotyczące przepełnienia stosu.
TylerH,
122

Rozwiązanie, które ja zawsze używany był do pierwszego zanikają, a następnie kurczą się font-size, paddingi marginwartości. Nie wygląda tak samo jak ścieranie, ale działa bez zakłóceń heightlub max-height.

Przykład roboczy:

/* 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
źródło
Pracował dla mnie starannie. Nie do końca jak slajd jQuery w górę / w dół. Różnicę widać tylko w przypadku słabych procesorów, takich jak stare komputery lub urządzenia mobilne, oraz otwieranie i zamykanie wielu z nich jednocześnie.
Cruz Nunez,
3
Jeśli masz przyciski lub inne elementy nietekstowe, skończysz z niechcianymi białymi spacjami, nie poruszając się.
Dennis W
3
Możesz dalej go udoskonalać, wybierając wszystkie elementy potomne *i stosując inne zmiany. Jednak mój powyższy kod powinien mieć wpływ na przyciski, jeśli były poprawnie stylizowane em.
Steven Vachon
1
W moim przypadku musiałem również dodać line-height: 0;iborder-width: 0;
Andrey Shatilov
1
Nie trzeba dostosowywać, line-heightjeśli poprawnie unikniesz jednostek o wartości: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon
93

Wiem, że to trzydziesta odpowiedź na to pytanie, ale myślę, że warto, więc proszę. Jest to rozwiązanie tylko do obsługi CSS o następujących właściwościach:

  • Na początku nie ma opóźnienia, a przejście nie kończy się wcześnie. W obu kierunkach (rozwijanie i zwijanie), jeśli określisz czas trwania przejścia w CSS na 300 ms, przejście zajmie 300 ms.
  • Przenosi rzeczywistą wysokość (w przeciwieństwie do transform: scaleY(0)), więc robi to dobrze, jeśli po składanym elemencie jest treść.
  • While (podobnie jak w innych rozwiązaniach) tam numery Magic (typu „pick długość, która jest większa niż twoja skrzynka jest kiedykolwiek będzie”), to nie jest śmiertelne, jeśli kończy się na tym, założenie błędne. W takim przypadku przejście może nie wyglądać niesamowicie, ale przed przejściem i po nim nie stanowi to problemu: w stanie rozwiniętym ( height: auto) cała zawartość zawsze ma odpowiednią wysokość (w przeciwieństwie do np. Wybrania takiego, max-heightktóry okazuje się być za nisko). A w stanie złożonym wysokość wynosi zero, tak jak powinna.

Próbny

Oto demo z trzema składanymi elementami, o różnych wysokościach, które wykorzystują ten sam CSS. Możesz kliknąć „całą stronę” po kliknięciu „uruchom snippet”. Zauważ, że JavaScript przełącza tylko collapsedklasę CSS, nie wymaga żadnego pomiaru. (Możesz zrobić dokładnie to demo bez JavaScript, używając pola wyboru lub :target). Zauważ też, że część CSS odpowiedzialna za przejście jest dość krótka, a HTML wymaga tylko jednego dodatkowego elementu opakowania.

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

Jak to działa?

W rzeczywistości dwie zmiany są zaangażowane w realizację tego. Jedna z nich zmienia stan margin-bottomz 0px (w stanie rozwiniętym) na -2000pxstan zwinięty (podobny do tej odpowiedzi ). 2000 tutaj jest pierwszą magiczną liczbą, opiera się na założeniu, że twoje pudełko nie będzie wyższe niż to (2000 pikseli wydaje się rozsądnym wyborem).

margin-bottomSamo użycie przejścia ma dwa problemy:

  • Jeśli faktycznie masz pudełko większe niż 2000 pikseli, to margin-bottom: -2000pxnie ukryje wszystkiego - będą widoczne rzeczy nawet w zwiniętej obudowie. To drobna poprawka, którą zrobimy później.
  • Jeśli rzeczywiste pole ma, powiedzmy, 1000 pikseli wysokości, a twoje przejście ma 300 ms długości, wówczas widoczne przejście jest już zakończone po około 150 ms (lub, w przeciwnym kierunku, zaczyna się 150 ms późno).

Naprawienie drugiego problemu polega na tym, że pojawia się drugie przejście, a to przejście koncepcyjnie jest ukierunkowane na minimalną wysokość opakowania („koncepcyjnie”, ponieważ tak naprawdę nie używamy min-heightdo tego właściwości; więcej na ten temat później).

Oto animacja pokazująca, jak połączenie przejścia dolnego marginesu z przejściem minimalnej wysokości, oba o jednakowym czasie trwania, daje nam połączone przejście z pełnej wysokości do zera wysokości, które ma taki sam czas trwania.

animacja jak opisano powyżej

Lewy pasek pokazuje, w jaki sposób ujemny dolny margines popycha dno w górę, zmniejszając widoczną wysokość. Środkowy pasek pokazuje, w jaki sposób minimalna wysokość zapewnia, że ​​w przypadku zwijania przejście nie kończy się wcześnie, aw przypadku rozszerzania przejście nie zaczyna się późno. Prawy pasek pokazuje, w jaki sposób połączenie tych dwóch powoduje przejście skrzynki z pełnej wysokości do zera wysokości w odpowiednim czasie.

W przypadku mojej wersji demonstracyjnej zdecydowałem się na 50 pikseli jako górną minimalną wartość wysokości. To druga magiczna liczba, która powinna być niższa niż kiedykolwiek wysokość pudełka. 50 pikseli również wydaje się rozsądne; wydaje się mało prawdopodobne, że bardzo często chciałbyś, aby element składany nie miał nawet 50 pikseli wysokości.

Jak widać na animacji, powstałe przejście jest ciągłe, ale nie można go rozróżnić - w momencie, gdy minimalna wysokość jest równa pełnej wysokości skorygowanej o dolny margines, następuje nagła zmiana prędkości. Jest to bardzo widoczne w animacji, ponieważ wykorzystuje liniową funkcję synchronizacji dla obu przejść i ponieważ całe przejście jest bardzo wolne. W rzeczywistym przypadku (moje demo u góry) przejście zajmuje tylko 300 ms, a przejście dolnego marginesu nie jest liniowe. Grałem z wieloma różnymi funkcjami pomiaru czasu dla obu przejść, a te, w których skończyłem, wydawały się działać najlepiej w najróżniejszych przypadkach.

Do rozwiązania pozostają dwa problemy:

  1. punkt z góry, w którym pola o wysokości większej niż 2000 pikseli nie są całkowicie ukryte w stanie zwiniętym,
  2. i odwrotny problem, gdzie w przypadku nie ukrytym, pola o wysokości mniejszej niż 50 pikseli są zbyt wysokie, nawet gdy przejście nie jest uruchomione, ponieważ minimalna wysokość utrzymuje je na 50 pikseli.

Pierwszy problem rozwiązujemy, nadając elementowi kontenerowi max-height: 0zwiniętą skrzynkę z 0s 0.3sprzejściem. Oznacza to, że tak naprawdę nie jest to przejście, ale max-heightjest stosowane z opóźnieniem; ma zastosowanie tylko po zakończeniu przejścia. Aby to działało poprawnie, musimy również wybrać wartość liczbową max-heightdla przeciwnego, nie zwiniętego stanu. Ale w przeciwieństwie do przypadku 2000px, w którym wybranie zbyt dużej liczby wpływa na jakość przejścia, w tym przypadku tak naprawdę nie ma znaczenia. Możemy więc wybrać liczbę, która jest tak wysoka, że wiemy, że żadna wysokość nigdy nie zbliży się do tego. Wybrałem milion pikseli. Jeśli uważasz, że możesz potrzebować obsługiwać zawartość o wysokości większej niż milion pikseli, to 1) Przepraszam i 2) po prostu dodaj kilka zer.

Drugi problem jest powodem, dla którego tak naprawdę nie używamy min-heightdo przejścia minimalnej wysokości. Zamiast tego ::afterw kontenerze znajduje się pseudoelement, heightktóry zmienia się z 50px na zero. Ma to taki sam efekt jak min-height: Nie pozwoli, aby pojemnik zmniejszył się poniżej jakiejkolwiek wysokości, jaką aktualnie ma pseudoelement. Ale ponieważ używamy height, nie min-height, możemy teraz użyć max-height(ponownie zastosowane z opóźnieniem), aby ustawić rzeczywistą wysokość pseudoelementu na zero po zakończeniu przejścia, zapewniając, że przynajmniej poza przejściem nawet małe elementy mają prawidłowa wysokość. Ponieważ min-heightjest silniejszy niż max-height, nie działałoby to, gdybyśmy użyli kontenera min-heightzamiast pseudoelementuheight. Podobnie jak max-heightw poprzednim akapicie, max-heightwymaga to również wartości dla przeciwnego końca przejścia. Ale w tym przypadku możemy po prostu wybrać 50 pikseli.

Testowane w Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (z wyjątkiem problemu z układem Flexbox w mojej wersji demonstracyjnej, że nie zawracałem sobie głowy debugowaniem) i Safari (Mac, iOS ). Mówiąc o Flexboksie, powinno być możliwe, aby działało to bez użycia Flexboksa; w rzeczywistości myślę, że możesz sprawić, by prawie wszystko działało w IE7 - z wyjątkiem faktu, że nie będziesz mieć przejść CSS, co czyni je raczej bezcelowym ćwiczeniem.

balpha
źródło
35
Chciałbym, abyś mógł skrócić (uprościć) swoje rozwiązanie lub przynajmniej przykładowy kod - wygląda na to, że 90% przykładowego kodu nie ma związku z odpowiedzią.
Gil Epshtain
Czy istnieje sposób, aby te przejścia działały również, jeśli collapsible-wrapperpozycja ma pozycję bezwzględną. Element overflow: hiddennadrzędny jest wymagany do przejścia, ale koliduje z absolutnie pozycjonowanymi elementami.
pmkro,
1
@pmkro Tak, ja też miałem ten problem. Rozwiązałem go za pomocą clip-path: polygon(...)zamiast overflow: hidden, ponieważ w przeciwieństwie do drugiego, możesz zmienić pierwszy. Jako rezerwę dla starszych przeglądarek użyłem dodatkowej transformacji skalowania. Zobacz komentarz na górze github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha
2
Miły. To działa naprawdę dobrze. Powinna być zaakceptowana odpowiedź
Henry Ing-Simmons
83

Możesz, z odrobiną nie-semantycznej jiggery-pokery. Moje zwykłe podejście polega na animowaniu wysokości zewnętrznego DIV, który ma jedno dziecko, które jest DIV bez stylu używanym tylko do pomiaru wysokości zawartości.

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>

Chciałoby się po prostu zrezygnować .measuringWrapperi ustawić wysokość DIV na auto i mieć animację, ale wydaje się, że to nie działa (wysokość zostaje ustawiona, ale nie ma animacji).

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>

Moja interpretacja jest taka, że ​​do uruchomienia animacji potrzebna jest wyraźna wysokość. Nie można uzyskać animacji wysokości, gdy jest to wysokość (wysokość początkowa lub końcowa) auto.

jhurshman
źródło
10
Ponieważ zależy to od javascript, możesz również łatwo dodać pomiarWrapper za pomocą javascript!
Quickredfox
18
Możesz to zrobić bez opakowania. Po prostu: function growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
user1742529
8
Sprawdź to ... porozmawiaj o prostej odpowiedzi jsfiddle.net/n5XfG/2596 ... Twoja odpowiedź jest niesamowicie złożona bez uzasadnionego powodu. Nie ma potrzeby max-height, nie ma potrzeby auto, a zwłaszcza nie ma potrzeby korzystania z Javascript !! Tylko dwie proste deklaracje
VIDesignz
12
@VIDesignz Animujesz ponad margines: 100%. To 100% szerokości elementu, a nie jego wysokości! Oznacza to, że jeśli masz element o wysokości większej niż jego szerokość, to go nie ukryje. Ponadto, rzeczywista prędkość animacji jest zupełnie inny niż ten, który jest zdefiniowany.
Timo Türschmann
3
Byłem podekscytowany odpowiedzią VIDesignz, dopóki nie przeczytałem więcej o komentarzu Timo. Tak, margin-top(i margin-bottom) są określone w stosunku do szerokości, gdy są zdefiniowane w procentach, tak, to głupie, ale wyjaśnia również, dlaczego czasy otwierania i zamykania animacji są zupełnie inne - to to samo, co widzisz w najlepiej ocenianej odpowiedzi, określając trudny -kodowana maksymalna wysokość. Dlaczego tak trudno to zrobić dobrze?
Coderer
52

Wizualnym obejściem animacji wysokości za pomocą przejść CSS3 jest zamiast tego animacja wypełnienia.

Nie do końca uzyskujesz pełny efekt czyszczenia, ale zabawa z czasem przejścia i wartościami wypełnienia powinna sprawić, że będziesz wystarczająco blisko. Jeśli nie chcesz jawnie ustawiać wysokości / maksymalnej wysokości, powinno być to, czego szukasz.

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 off stephband's powyżej jsFiddle)

Katharsis
źródło
3
Myślę, że to najlepsza odpowiedź. Ta technika może rozciągać się również na dopasowanie wyściółki u dzieci. W moim przypadku byłem w stanie połączyć to z wyraźnymi przejściami wysokości na niektórych ujawnianych treściach, a całkowity efekt jest znacznie bardziej skuteczny niż obejście maksymalnej wysokości, co jest dobre dla ujawnienia, ale wprowadza niezręczny moment opóźnienie w ukryciu. Wcześniej nie animowałbym wcale, niż wprowadzać bezsensowne opóźnienie, które sprawia, że ​​moja aplikacja wydaje się nie reagować. Jestem zaskoczony, że tak wielu ludzi uważa, że ​​jest to do przyjęcia.
Semicolon
17
Tyle że tutaj wcale nie animujesz wysokości. Animujesz dopełnienie ... może zniknąć dobrze, ponieważ może animować od bieżącego stanu do zera, ale jeśli przyjrzysz się uważnie, gdy się rozszerzy, otworzy się on z tekstem, a następnie dopełnienie tylko animuje ... ponieważ nie działa nie umie animować od 0 do auto .... potrzebuje zakresu liczbowego ... tak działa animacja.
Rob R
Jest to dobre obejście, które nie jest zhackowane. To nie jest najlepsza odpowiedź na to pytanie, ale jest to dla mnie alternatywa. Czasami potrzebujesz tylko efektu jakiejś animacji wysokości, a nie pełnej animacji :)
Ivan Durst
To rozwiązanie kończy się niepowodzeniem również w przypadku korzystania z opóźnienia przejścia.
Michel Joanisse
Zgadzam się również, że to rozwiązanie jest czystym obejściem, które zapewnia dość płynne przejścia, jeśli twoja animacja jest wystarczająco szybka (w moim przypadku dla akordeonu z przejściami 0,1 s jest idealna!). Nie będzie pasować do wielu aplikacji, ale inni też nie odpowiedzą na to pytanie!
Remi D
46

Moim obejściem jest przeniesienie maksymalnej wysokości na dokładną wysokość zawartości, aby uzyskać płynną animację, a następnie użyć wywołania zwrotnego przejścia, aby ustawić maksymalną wysokość na 9999 pikseli, aby zawartość mogła swobodnie się zmieniać.

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
źródło
14
@ Derija93 To używa zwykłych starych animacji limitu czasu javascript. Wyzwanie polega na użyciu przejścia CSS3.
Adam
3
No tak. Ale dlaczego miałbyś zamiast tego używać „przejść CSS3”, jeśli w swoim przykładzie używasz jQuery, biblioteki, która i tak zawiera dużo kodu „pisz mniej, rób więcej”? Chciałem zauważyć, że twoja wersja może wyglądać znacznie lepiej, nawet jeśli bardziej skomplikowana, gdyby NIE korzystała z jQuery, więc mogłaby działać na praktycznie każdej stronie internetowej. Chyba źle to sformułowałem. Przepraszam za to. ;)
Kiruse,
27
@ Derija93 Być może dlatego, że przejścia CSS3 mają (według wszystkich dostępnych źródeł) znacznie lepszą wydajność niż animacje jQuery. (Właściwie ma to ogromne znaczenie dla mojego obecnego przypadku użycia, który mnie tu przywiódł.)
Mark Amery
4
@ Derija93 'Ponieważ animacje JavaScript działają wolno w porównaniu do bro przejścia CSS3, a przy animacjach jQuery musisz się martwić o pętlę animacji. Animuj coś po kliknięciu, a następnie kliknij szybko i obserwuj, jak animacje się powtarzają. Jest to obsługiwane przez CSS3.
AlbertEngelB
2
@ Dropped.on.Caprica To już trochę stare. Powiedziałem już, że źle zrozumiałem koncepcję, którą próbował wykazać. Tak czy inaczej, w przypadku prostych animacji .stoprozwiązuje problem dość złożonego problemu z pętlą animacji. Teraz, kiedy animujesz wysokość i kolor, ale chcesz przerwać tylko animację wysokości, sprawy stają się trochę trudniejsze ... Rozumiem.
Kiruse
43

Przyjęta odpowiedź działa w większości przypadków, ale nie działa dobrze, gdy divwysokość może się znacznie różnić - szybkość animacji nie zależy od rzeczywistej wysokości zawartości i może wyglądać na nierówną.

Nadal możesz wykonywać rzeczywistą animację za pomocą CSS, ale musisz użyć JavaScript, aby obliczyć wysokość elementów, zamiast próbować użyć auto. JQuery nie jest wymagane, ale może być nieco zmodyfikowane, jeśli chcesz kompatybilności (działa w najnowszej wersji 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 Waszkiewicz
źródło
Może to być pomocne, ale nie mogę powiedzieć, ponieważ nie wiem, jak go używać bez lepszego przykładu.
Ivan Durst
1
Po prostu przekazujesz dowolny element DOM (na przykład z document.getElementById('mydiv')lub $('#mydiv').get()), a funkcja przełącza się między ukrywaniem / pokazywaniem go. Jeśli skonfigurujesz przejścia CSS, element będzie animowany automatycznie.
Oleg Waszkiewicz
Dodałem fragment kodu. Ma to tę zaletę, że działa dobrze w przypadku treści o dynamicznym rozmiarze.
Oleg Waszkiewicz
3
-1, ponieważ nie jest to „używanie CSS”. Pytanie dotyczy rozwiązania wolnego od JS. Myślę, że „poprawna” odpowiedź, to nie hack, byłaby jednak taka ...
dudewad
16
Głosowanie za używanie JS - poważnie? Połowa odpowiedzi tutaj używa JS, więc twoje zdanie głosowe nie wydaje się uczciwe ani konieczne. Poza tym, ta odpowiedź wciąż wykorzystuje CSS do wykonania rzeczywistej animacji, co było celem IMO. Jedyną rzeczą, do której stosuje się JS, jest obliczenie rzeczywistej wysokości, ponieważ nie jest to możliwe przy użyciu czystego CSS (a ustawienie min-heightjak w oczekiwanej odpowiedzi powoduje problemy z szybkością animacji, gdy wielkość listy może się znacznie różnić).
Oleg Waszkiewicz
30

Niewiele wspominano o Element.scrollHeightwłaściwości, która może być tu przydatna i nadal może być używana z czystym przejściem CSS. Właściwość zawsze zawiera „pełną” wysokość elementu, niezależnie od tego, czy i jak jego zawartość przepełnia się w wyniku zwiniętej wysokości (np height: 0.).

Jako taki, dla height: 0(skutecznie całkowicie zwiniętego) elementu jego „normalna” lub „pełna” wysokość jest nadal łatwo dostępna dzięki jego scrollHeightwartości (niezmiennie długości piksela ).

Dla takiego elementu, zakładając, że ma już skonfigurowane przejście, takie jak np. (Używając uljak na oryginalne pytanie):

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

Możemy wywołać pożądaną animowaną „ekspansję” wysokości, używając tylko CSS, z czymś takim jak poniżej (przy założeniu, że ulzmienna odnosi się do listy):

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

Otóż ​​to. Jeśli musisz zwinąć listę, zrobi to jedna z dwóch następujących instrukcji:

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

Moim przypadku użycie obracało się wokół animacji wykazy nieznane i często znaczne długości, więc nie było wygodne osiadanie na arbitralne „wystarczająco duży” heightlub max-heightspecyfikacji i ryzykując odcięcia treści lub treści, które nagle potrzebują do przewijania (jeśli overflow: auto, na przykład) . Dodatkowo, łagodzenie i synchronizacja są zerwane z max-heightrozwiązaniami opartymi na, ponieważ zastosowana wysokość może osiągnąć swoją maksymalną wartość znacznie wcześniej, niż by max-heightto osiągnęło 9999px. Wraz ze wzrostem rozdzielczości ekranu długości pikseli 9999pxpozostawiają w moich ustach zły smak. Moim zdaniem to szczególne rozwiązanie rozwiązuje problem w elegancki sposób.

Wreszcie, mamy nadzieję, że przyszłe wersje autorów CSS będą musiały robić tego rodzaju rzeczy jeszcze bardziej elegancko - ponownie przyjrzyj się pojęciu wartości „obliczonych” vs. „użytych” i „rozwiązanych” i zastanów się, czy przejścia powinny dotyczyć obliczeń wartości, w tym przejścia z widthi height(które obecnie są traktowane w specjalny sposób).

amn
źródło
6
Nie znalazłeś go w innych odpowiedziach tutaj, ponieważ interakcja z DOM przy użyciu tej Element.scrollHeightwłaściwości wymaga JavaScript i, jak jasno wynika z pytania, chcą to zrobić bez JavaScript.
TylerH,
3
Istnieje wiele innych części CSS innych niż te dwie pseudoklasy, które mogą rozwiązać ten problem, jak pokazują najwyżej ocenione odpowiedzi na stronie 1. Ustawienie stylu w JavaScript nie jest „czystym CSS”, ponieważ wymaga przede wszystkim ustawienia JS. Często, gdy dana osoba prosi o rozwiązanie oparte wyłącznie na CSS, dzieje się tak dlatego, że nie jest ona w stanie wstrzyknąć kodu JavaScript lub nie pozwala na to jego obecny projekt lub rola.
TylerH,
29

Używaj max-heightz innym łagodzeniem przejścia i opóźnieniem dla każdego stanu.

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

Zobacz przykład: http://jsfiddle.net/0hnjehjc/1/

Malihu
źródło
12
Problem polega na tym, że aby zapewnić wystarczającą ilość miejsca w dynamicznym środowisku, musisz użyć niedorzecznych wysokości 999999px. To z kolei spowoduje przesunięcie animacji, ponieważ ramki są obliczane na podstawie tej wartości. Możesz więc skończyć z „opóźnieniem” animacji w jednym kierunku i naprawdę szybkim zakończeniem w drugim (corr: funkcje czasowe)
Julian F. Weinert
29

Brak zakodowanych wartości.

Bez JavaScript.

Brak przybliżeń.

Sztuką jest użycie ukrytego i powielonego pliku, divaby przeglądarka zrozumiała, co oznacza 100%.

Ta metoda jest odpowiednia, gdy tylko możesz zduplikować DOM elementu, który chcesz animować.

.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
źródło
Świetnie wykonane - jest to prawidłowe rozwiązanie problemu, który tak naprawdę nie powinien istnieć. Animacja maksymalnej wysokości jest podstawowa, chyba że przejście jest ustawione na „liniowe”, jest również (oczywiście) bardzo złe w animowaniu treści CMS, w których wysokość jest zmienna.
M_Willett,
jest to sprytne, ale nie byłoby dla mnie wygodne kopiowanie elementów i treści DOM.
Andre Figueiredo
Możesz to zrobić bez kontenera i bardziej wydajnych animacji transform: scaleY(1), ponieważ to przejście nie usuwa przestrzeni.
Fabian von Ellerts
21

Gdy to piszę, jest już ponad 30 odpowiedzi, ale czuję, że moja odpowiedź poprawia się w stosunku do już zaakceptowanej odpowiedzi przez jake.

Nie byłem zadowolony z problemu, który wynika z prostego używania max-heighti przejścia CSS3, ponieważ jak zauważyło wielu komentujących, musisz ustawić swoją max-heightwartość bardzo blisko rzeczywistej wysokości, inaczej dostaniesz opóźnienie. Zobacz ten JSFiddle jako przykład tego problemu.

Aby obejść ten problem (wciąż nie używając JavaScript), dodałem kolejny element HTML, który przenosi transform: translateYwartość CSS.

Oznacza to jedno max-heighti drugie translateY: max-heightpozwala elementowi zepchnąć elementy znajdujące się pod nim, a jednocześnie translateYdaje pożądany efekt „natychmiastowy”. Problem z max-heightnadal istnieje, ale jego działanie jest zmniejszone. Oznacza to, że możesz ustawić znacznie większą wysokość dla swojej max-heightwartości i mniej się o nią martwić.

Ogólna korzyść polega na tym, że po przejściu z powrotem (zwinięciu) użytkownik translateYnatychmiast widzi animację, więc nie ma znaczenia, jak długo max-heightpotrwa.

Rozwiązanie jak skrzypce

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
źródło
Ładne porównanie @ Davidtaubmann, twój długopis bardzo dobrze ilustruje różnice! Poszedłem z translateYponad scalebo mi się nie podoba, jak scaledało taki efekt kompresji.
Andrew Messier
Dzięki @ andrew-messier, usunąłem komentarz, ponieważ jest błąd, tłumacz Y nie jest ekscytujący w codepen.io/davidtaubmann/pen/zKZvGP (ponieważ nie ma dziecka, które mogłoby się przeprowadzić, potrzebuje poprawki) ... ale w tym jeden możemy doskonale zobaczyć porównanie z TYLKO MAKSYMALNEJ WYSOKOŚCI i zmieszanie go ze skalą (jak podano w innych odpowiedziach): codepen.io/davidtaubmann/pen/jrdPER . To wszystko bardzo mi pomogło dzięki metodologii Target, której używam
DavidTaubmann,
18

Ok, więc myślę, że wymyśliłem super prostą odpowiedź ... nie max-height, używa relativepozycjonowania, działa na lielementach i jest czystym CSS. Nie testowałem w niczym innym niż Firefox, choć sądząc po CSS, powinien działać na wszystkich przeglądarkach.

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
źródło
13
Będzie to działać, dopóki wysokość elementu nie przekroczy jego szerokości. Jest to podstawa obliczania marginesów za pomocą wartości procentowych: są one obliczane na podstawie szerokości elementu. Jeśli więc masz element o szerokości 1000 pikseli, element o wielkości 1100 pikseli będzie zbyt duży, aby to rozwiązanie zadziałało, co oznacza, że ​​musisz zwiększyć ten ujemny górny margines. Zasadniczo jest to dokładnie ten sam problem, co użycie wysokości lub maksymalnej wysokości.
dudewad
15

EDYCJA: Przewiń w dół, aby uzyskać zaktualizowaną odpowiedź
Robiłem listę rozwijaną i widziałem ten post ... wiele różnych odpowiedzi, ale postanawiam również udostępnić moją listę rozwijaną, ... To nie jest idealne, ale przynajmniej będzie używać tylko css dla upuścić! Używam transform: translateY (y), aby przekształcić listę do widoku ...
Możesz zobaczyć więcej w teście
http://jsfiddle.net/BVEpc/4/
Umieściłem div za każdym li, ponieważ mój rozwijana lista nadchodzi z góry i aby je odpowiednio pokazać, to było potrzebne, mój kod div to:

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

a najechanie to:

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

a ponieważ wysokość ul jest ustawiona na zawartość, którą może przeniknąć przez zawartość twojego ciała, dlatego zrobiłem to dla ul:

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

i najedź:

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

drugi raz po przejściu jest opóźnienie i zostanie ukryte po tym, jak moja lista rozwijana zostanie animacyjnie zamknięta ...
Mam nadzieję, że później ktoś skorzysta z tego.

EDYCJA: Po prostu nie mogę uwierzyć, że ppl faktycznie używa tego prototypu! to menu rozwijane jest tylko dla jednego podmenu i to wszystko !! Zaktualizowałem lepszy, który może mieć dwa podmenu dla kierunku ltr i rtl z obsługą IE 8.
Fiddle dla LTR
Fiddle dla RTL Mam
nadzieję, że ktoś uzna to za przydatne w przyszłości.

Sijav
źródło
11

Możesz przejść z wysokości: 0 na wysokość: auto, pod warunkiem, że podasz również wysokość minimalną i maksymalną.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
Stuart Badminton
źródło
@Stuart Badminton czy możesz podać działający przykład? Złożyłem
Misha Reyzlin
5
Problem dotyczy reguły przejścia. aby to zadziałało, należy również zastosować przejście na wysokość minimalną i maksymalną. Przejście: wysokość .5s liniowy min wysokości .5s liniowy, Max-wysokość .5s liniowy
Stuart Badminton
2
oto, co mam później, tak jak powiedziałeś, potrzebne do animowania maksymalnej wysokości: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin
11
Oczywiście jest z tym problem. Czas potrzebny do animacji maksymalnej wysokości nie jest czasem przejścia do automatycznej wysokości ramki: dociera do wysokości: najpierw automatycznej, zanim animacja maksymalnej wysokości zakończy działanie. Podobnie, przy przejściu na 0, przejście nie rozpoczyna się natychmiast, ponieważ maksymalna wysokość zaczyna się na wysokości większej niż wysokość: auto. Przejście do maksymalnej wysokości: 1000
pikseli
Masz rację, podczas dalszych testów zauważyłem to. Nie wiem, czy pełna wysokość: od 0 do wysokości: auto zostanie kiedykolwiek zaimplementowana, ale użycie maksymalnej wysokości w niektórych sytuacjach rozwiązuje sytuację niemożliwą w inny sposób przy użyciu samego CSS.
Stuart Badminton
10

Rozwiązanie Flexbox

Plusy:

  • prosty
  • bez JS
  • płynne przejście

Cons:

  • element należy umieścić w elastycznym pojemniku o stałej wysokości

Sposób działania polega na tym, że zawsze ma flex-base: auto na elemencie z zawartością, a zamiast tego przenosi flex-grow i flex-shrink.

Edycja: Ulepszony JS Fiddle inspirowany interfejsem Xbox One.

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

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

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

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

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

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

JS Fiddle

Lee Comstock
źródło
7

Rozwijając odpowiedź @ jake, przejście przejdzie do wartości maksymalnej wysokości, powodując niezwykle szybką animację - jeśli ustawisz przejścia dla obu: najechania myszką i wyłączenia, możesz nieco bardziej kontrolować szaloną prędkość.

Tak więc li: hover ma miejsce, gdy mysz wchodzi w stan, a następnie przejściem do właściwości niehoversowanej będzie pozostawienie myszy.

Mam nadzieję, że będzie to pomocne.

na przykład:

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

Oto skrzypce: http://jsfiddle.net/BukwJ/

Jamie
źródło
1
Nie wiem, dlaczego otrzymało -1. Myślę, że to lepsza próba niż niektórzy ludzie dodający mnóstwo javascript. Nie jest to idealne rozwiązanie, ale z pewnymi poprawkami robi dobrą robotę, znalazłem, że działa to dla wartości zwracanej. przejście: wszystkie 500 ms bez sześciennych (0,000, 1,225, 0,085, 0,385); Było to oparte na przejściu 2s po otwarciu, a następnie powyższym kodzie, aby wrócić do stanu początkowego. Dzięki ... Pomogło mi to lepiej niż wszystkie powyższe. +1 Użyłem tej strony do stworzenia bezier matthewlein.com/ceaser
gcoulby
OK, zajęło to trochę więcej ulepszeń. przejście: wszystkie 500 ms bez sześciennych (0,000, 1,390, 0,000, 0,635); Powinienem określić, że moje przejście wynosi od 5% do 100% (ale w rzeczywistości wartość końcowa wynosi około 28%), więc zależy to od projektu.
gcoulby
Oczywiście, nie jest idealny, ale jest szybkim rozwiązaniem, jeśli wiesz, jaka będzie średnia wysokość.
jamie
7

Myślę, że wymyśliłem naprawdę solidne rozwiązanie

OK! Wiem, że ten problem jest tak stary jak Internet, ale myślę, że mam rozwiązanie, które zmieniłem w wtyczkę o nazwie mutant-przejście . Moje rozwiązanie ustawia style=""atrybuty śledzonych elementów za każdym razem, gdy następuje zmiana w DOM. efekt końcowy jest taki, że możesz używać dobrego ole CSS do swoich przejść i nie używać hacky poprawek lub specjalnego javascript. Jedyne, co musisz zrobić, to ustawić za pomocą tego, co chcesz śledzić na danym elemencie data-mutant-attributes="X".

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

Otóż ​​to! To rozwiązanie wykorzystuje MutationObserver do śledzenia zmian w DOM. Z tego powodu tak naprawdę nie musisz niczego konfigurować ani używać javascript do ręcznej animacji. Zmiany są śledzone automatycznie. Ponieważ jednak używa MutationObserver, nastąpi to tylko w IE11 +.

Skrzypce!

JonTroncoso
źródło
12
Zwykle, gdy ktoś pyta, jak zrobić coś „bez JavaScript”, oznacza to, że rozwiązanie musi działać w przeglądarkach z wyłączoną obsługą JavaScript. To nie znaczy „bez pisania własnych skryptów”. Stwierdzenie, że wtyczki można używać bez użycia JavaScript, jest co najmniej mylące - biorąc pod uwagę, że jest to wtyczka JavaScript.
BoltClock
Powinienem wyjaśnić, że kiedy mówię, że nie musisz używać żadnego specjalnego javascript, mam na myśli, że nie musisz pisać żadnego javascript. wystarczy dołączyć bibliotekę JS i określić, które atrybuty chcesz oglądać w kodzie HTML. Nie musisz używać css o stałej wysokości ani niczego wymyślać. po prostu styl i idź.
JonTroncoso,
„(...) tak naprawdę nie musisz niczego konfigurować ani używać javascript do ręcznej animacji rzeczy (...)”, więc używasz JavaScript do zabawy
Roko C. Buljan
6

Oto sposób na przejście z dowolnej wysokości początkowej, w tym 0, na automatyczną (pełny rozmiar i elastyczny) bez konieczności ustawiania kodu dla poszczególnych węzłów lub dowolnego kodu użytkownika do inicjowania: https://github.com/csuwildcat / transit-auto . To jest w zasadzie święty graal tego, czego chcesz, wierzę -> http://codepen.io/csuwldcat/pen/kwsdF . Wystarczy uderzyć następujący plik JS na swoją stronę, a wszystko, co musisz zrobić, to dodać / usunąć pojedynczy atrybut logiczny - reveal=""- z węzłów, które chcesz rozwinąć i skurczyć.

Oto wszystko, co musisz zrobić jako użytkownik, po dołączeniu bloku kodu znajdującego się poniżej przykładowego kodu:

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

Oto blok kodu, który należy umieścić na stronie, a potem wszystko jest sos:

Upuść ten plik JS na swojej stronie - wszystko to po prostu działa

/ * Kod wysokości: auto; przejście * /

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

/ * Kod dla 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
źródło
9
Chociaż jest to znacznie czystsze i elastyczne rozwiązanie niż inne, które zostały zasugerowane, osobiście uważam, że nie powinno to być coś, co JS powinien w ogóle dotknąć. Nie mówię, że się mylisz, to tylko westchnienie.
mystrdat
@mystrdat, aby być jasnym, JS służy tylko do obserwowania określonych warunków i dodawania kilku atrybutów / tymczasowych wartości CSS w oparciu o zdarzenia / warunki, na które patrzy. Przejście wciąż odbywa się w CSS. Choć się zgadzam, to smutne, że ten poziom przygotowania i trzymania za rękę jest konieczny, aby naprawdę zaspokoić pozornie prosty przypadek użycia! : /
csuwldcat,
1
@KarolisRamanauskas nie, to nie jest problem - IE obsługuje zarówno klatki kluczowe CSS, jak i requestAnimationFrame. Użyłem clipjako wyzwalacza właściwości manekina iz jakiegoś powodu IE przestał pozwalać na animację klipu. Przełączyłem się na nieprzezroczystość i wszystko znowu działa: codepen.io/csuwldcat/pen/FpzGa . Zaktualizowałem kod w odpowiedzi, aby pasował.
csuwldcat
1
@csuwldcat Bardzo fajne rozwiązanie, zauważyłem, że treść pojawia się i znika na niewielką część, dlaczego tak się dzieje? Czy można tego uniknąć?
Beto Aveiga,
12
Chcę to zagłosować, ale zamiast odpowiedzieć na pytanie, jak sprawić, by działało, udostępniłeś napisaną przez siebie wtyczkę, aby działała. My, ciekawi nas, pozostajemy w stanie odtworzyć wtyczkę, co nie jest zbyt zabawne. Chciałbym, żebyś zaktualizował swoją odpowiedź, aby zawierała więcej wyjaśnień na temat tego, co robi Twoja wtyczka i dlaczego. Zmień kod, aby był bardziej objaśniający. Na przykład masz całą sekcję kodu, która po prostu zapisuje statyczny CSS. Wolę zobaczyć CSS, niż kod, który go generuje. Możesz pominąć nudne części, takie jak powtarzanie dla wszystkich prefiksów przeglądarki.
gilly3
6

Odpowiedź Jake'a na animację maksymalnej wysokości jest świetna, ale stwierdziłem, że opóźnienie spowodowane ustawieniem dużej maksymalnej wysokości jest denerwujące.

Można przenieść składaną zawartość do wewnętrznego div i obliczyć maksymalną wysokość, uzyskując wysokość wewnętrznego div (poprzez JQuery będzie to 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');
});

Oto link jsfiddle: http://jsfiddle.net/pbatey/duZpT

Oto jsfiddle z absolutnie minimalną wymaganą ilością kodu: http://jsfiddle.net/8ncjjxh8/

pbatey
źródło
5

Wiem, że ten wątek się starzeje, ale w niektórych wyszukiwaniach Google zajmuje wysoką pozycję, więc uważam, że warto go zaktualizować.

Możesz także pobrać / ustawić własną wysokość elementu:

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

Powinieneś zrzucić ten Javascript zaraz po tagu zamykającym target_box w wewnętrznym tagu skryptowym.

Charley
źródło
document.getElementById ('target_box'). getBoundingClientRect (). wysokość powinna być najlepsza.
Zmień
5

Byłem w stanie to zrobić. Mam .child& A .parentdiv. Dzielenie dziecka idealnie pasuje do szerokości / wysokości rodzica z absolutepozycjonowaniem. Następnie animuję translatewłaściwość, aby obniżyć jej Ywartość 100%. Jego bardzo płynna animacja, bez wad i wad, jak każde inne rozwiązanie tutaj.

Coś w tym rodzaju, pseudo kod

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

Byłoby podasz heightna .parent, w px, %lub pozostawić jako auto. Ten div następnie maskuje .childdiv, gdy zsunie się.

Josh Ribakoff
źródło
Przykład roboczy tego będzie mile widziany.
Neurotransmitter
5

Wysłałem odpowiedź z JavaScriptem i przegłosowałem, więc zirytowałem się i spróbowałem ponownie, i złamałem ją tylko za pomocą CSS!

To rozwiązanie wykorzystuje kilka „technik”:

  • padding-bottom:100%„hack”, w którym wartości procentowe są zdefiniowane w kategoriach bieżącej szerokości elementu. Więcej informacji na temat tej techniki.
  • float owijanie termokurczliwe, (wymagając dodatkowego diva, aby zastosować hack czyszczenia pływaka)
  • niekonwencjonalne użycie https://caniuse.com/#feat=css-writing-mode i kilka transformacji, aby go cofnąć (pozwala to na użycie powyższego włamania do paddingu w kontekście pionowym)

Rezultatem jest to, że uzyskujemy wydajne przejścia tylko przy użyciu CSS i jedną funkcję przejścia, aby płynnie osiągnąć przejście; Święty Gral!

Oczywiście, ma to wadę! Nie mogę wymyślić, jak kontrolować szerokość odcinania zawartości ( overflow:hidden); ze względu na hack z wyściółką szerokość i wysokość są ściśle powiązane. Może być jednak jakiś sposób, więc wrócę do tego.

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>

Eoghan
źródło
Inną wadą tego jest to, że nie można przesunąć kursora z divu „hoverme” na listę przedmiotów w celu interakcji z nimi (pozornie powód zapytania OP), więc jest to przydatne tylko jako funkcja podpowiedzi, a nie , powiedzmy, menu z zagnieżdżonymi podmenu.
TylerH,
Jest to wyłącznie konsekwencja sposobu sformułowania pytania i nie jest związana z techniką. Możesz zmienić wyzwalacz na input[type="checkbox"]:checkedzamiast zamiast, :hoverjeśli chcesz (tzn. Nie chcesz używać JavaScript do dodawania żadnych klas)
EoghanM
Chodzi mi o to, że niezależnie od sposobu sformułowania pytania, jeżeli implementacja rozwiązania jest tak ograniczona, prawdopodobnie należy o tym wspomnieć w odpowiedzi na to rozwiązanie (lub najlepiej uwzględnić ). Wspomniałeś, że to rozwiązanie jest „świętym Graalem”, ale nie można nawet ukryć przemienionego diva… nie wydaje mi się to tak ostatecznym rozwiązaniem.
TylerH,
Cześć @TylerH, przepraszam, przeoczyłem fakt, że wyzwalacz w oryginalnym pytaniu otacza rozszerzoną listę, więc lista powinna pozostać otwarta po najechaniu myszką. Zaktualizowałem swoją odpowiedź, aby to odzwierciedlić. Jak już wspomniałem, sposób, w jaki jest uruchamiany, nie jest aż tak niemądry, ponieważ przedstawiam nową technikę, która wcześniej nie była uważana za możliwą tylko w CSS (szukałem rozwiązania od kilku lat).
EoghanM,
4

Oto rozwiązanie, którego właśnie użyłem w połączeniu z jQuery. Działa to dla następującej struktury 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>

i funkcja:

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

Następnie można użyć funkcji kliknięcia, aby ustawić i usunąć wysokość za pomocą 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
źródło
to nie pokazuje się poprawnie w najnowszej wersji jquery
oliverbachmann,
4

Ja niedawno przejście max-heightna lielementy zamiast owijania ul.

Rozumowanie to, że opóźnienie dla małych max-heightsjest znacznie mniej zauważalny (jeśli w ogóle) w porównaniu do dużych max-heights, i mogę też ustawić moje max-heightwartości względem font-sizez lianiżeli jakiejś arbitralnej ogromnej liczby za pomocą emslub rems.

Jeśli mój rozmiar czcionki to 1rem, ustawię max-heightna coś podobnego 3rem(w celu dostosowania owiniętego tekstu). Możesz zobaczyć przykład tutaj:

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

uważność
źródło
3

Nie przeczytałem wszystkiego szczegółowo, ale ostatnio miałem ten problem i zrobiłem, co następuje:

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

Dzięki temu możesz mieć div, który na początku pokazuje zawartość do wysokości 200 pikseli, a po najechaniu myszą jego rozmiar staje się co najmniej tak wysoki, jak cała zawartość div. Div nie staje się 3000 pikseli, ale 3000 pikseli to limit, który narzucam. Upewnij się, że masz przejście na non: hover, w przeciwnym razie możesz uzyskać dziwne renderowanie. W ten sposób:: hover dziedziczy po non: hover.

Przejście nie działa z px na% lub na auto. Musisz użyć tej samej jednostki miary. To działa dobrze dla mnie. Korzystanie z HTML5 czyni go idealnym ....

Pamiętaj, że zawsze jest coś do obejrzenia ...; )

Mam nadzieję, że ktoś uzna to za przydatne

work.stella84
źródło
3

Rozwiązanie Jake dotyczące maksymalnej wysokości działa dobrze, jeśli podana na stałe wartość maksymalnej wysokości nie jest dużo większa niż rzeczywista wysokość (ponieważ w przeciwnym razie występują niepożądane opóźnienia i problemy z synchronizacją). Z drugiej strony, jeśli przypadkowo zakodowana wartość nie jest większa niż rzeczywista wysokość, element nie otworzy się całkowicie.

Poniższe rozwiązanie tylko CSS wymaga również zakodowanego rozmiaru, który powinien być większy niż większość występujących rzeczywistych rozmiarów. Jednak to rozwiązanie działa również, jeśli rzeczywisty rozmiar jest w niektórych sytuacjach większy niż rozmiar zakodowany na stałe. W takim przypadku przejście może nieco przeskoczyć, ale nigdy nie pozostawi częściowo widocznego elementu. To rozwiązanie może być również wykorzystane w przypadku nieznanej treści, np. Z bazy danych, w której po prostu wiesz, że zawartość zwykle nie jest większa niż x pikseli, ale są wyjątki.

Ideą jest użycie wartości ujemnej dla marginesu na dole (lub marginesu na górze dla nieco innej animacji) i umieszczenie elementu treści w środkowym elemencie z przepełnieniem: ukrytym. Ujemny margines elementu treści zmniejsza wysokość środkowego elementu.

Poniższy kod używa przejścia na marginesie od -150px do 0px. To samo działa dobrze, o ile element zawartości nie jest większy niż 150 pikseli. Ponadto wykorzystuje przejście na maksymalnej wysokości dla środkowego elementu od 0px do 100%. To w końcu ukrywa środkowy element, jeśli element zawartości jest większy niż 150 pikseli. W przypadku maksymalnej wysokości przejście służy jedynie do opóźnienia jego zastosowania o sekundę podczas zamykania, a nie do uzyskania płynnego efektu wizualnego (a zatem może działać w zakresie od 0px do 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>

Wartość dolnego marginesu powinna być ujemna i jak najbliższa rzeczywistej wysokości elementu treści. Jeśli (wartość absoute) jest większa, występują podobne problemy z opóźnieniem i taktowaniem jak w rozwiązaniach o maksymalnej wysokości, które mogą być jednak ograniczone, o ile rozmiar zakodowany na sztywno nie jest znacznie większy niż rzeczywisty. Jeśli wartość bezwzględna marginesu dolnego jest mniejsza niż rzeczywista wysokość, tansition skacze nieco. W każdym razie po przejściu element treści jest albo w pełni wyświetlany, albo całkowicie usunięty.

Aby uzyskać więcej informacji, zobacz mój post na blogu http://www.taccgl.org/blog/css_transition_display.html#combined_height

Helmut Emmelmann
źródło
3

Możesz to zrobić, tworząc odwrotną (zwiniętą) animację ze ścieżką przycinającą.

#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
źródło
2

To nie jest dokładnie „rozwiązanie” problemu, ale raczej obejście. Działa tylko tak, jak napisano z tekstem, ale jestem pewien, że można go zmienić na inne elementy.

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

Oto przykład: http://codepen.io/overthemike/pen/wzjRKa

Zasadniczo ustawiasz rozmiar czcionki na 0 i przenosisz ją zamiast wysokości, maksymalnej wysokości lub skali Y () itd. W wystarczająco szybkim tempie, aby wysokość mogła przekształcić się w żądane ustawienie. Przekształcenie rzeczywistej wysokości za pomocą CSS na auto nie jest obecnie możliwe, ale transformacja zawartej w niej treści jest, stąd przejście do rozmiaru czcionki.

  • Uwaga - w codepen JEST JEST skrypt javascript, ale jego jedynym celem jest dodanie / usunięcie klas css po kliknięciu dla akordeonu. Można to zrobić za pomocą ukrytych przycisków opcji, ale nie skupiałem się na tym, tylko na transformacji wysokości.
Mike S.
źródło
1
Skutecznie powiela tę odpowiedź , ale pomija efekt zanikania krycia.
SeldomNeedy,
Zgoda. Lepsze podejście.
Mike S.,