Como posso fazer a transição da altura: 0; à altura: auto; usando CSS?

2136

Estou tentando fazer um <ul>slide para baixo usando transições CSS.

O <ul>começa em height: 0;. Ao passar o mouse, a altura é definida como height:auto;. No entanto, isso está fazendo com que simplesmente apareça, não faça transição,

Se eu fizer isso de height: 40px;para height: auto;, ele deslizará para height: 0;e, de repente, pulará para a altura correta.

De que outra forma eu poderia fazer isso sem usar 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
fonte
Acredito que a height:auto/max-heightsolução só funcionará se você estiver expandindo a área maior do que a heightque deseja restringir. Se você tem um menu suspenso max-heightde 300px, mas uma caixa de combinação, que pode retornar 50pxe max-heightnão ajudá-lo, 50pxé variável dependendo do número de elementos, você pode chegar a uma situação impossível em que eu não posso consertá-lo porque heightnão é fixo, height:autofoi a solução, mas não posso usar transições com isso.
Christopher Thomas
51
O OP está tentando a solução css, não o js, ​​caso contrário, eles poderiam simplesmente usar o estouro e animar
Toni Leigh
5
@VIDesignz: Mas a parte superior da margem de -100% da div interna recebe a largura da div do wrapper, não a altura. Portanto, esta solução tem o mesmo tipo de problema que a solução de altura máxima. Além disso, quando a largura é menor que a altura do conteúdo, nem todo o conteúdo fica oculto em -100% da margem superior. Portanto, esta é uma solução errada.
GregTom
1
Consulte github.com/w3c/csswg-drafts/issues/626 para obter uma discussão sobre as especificações e a implementação de uma solução adequada
Ben Creasy

Respostas:

2745

Use max-heightna transição e não height. E defina um valor max-heightpara algo maior do que sua caixa jamais receberá.

Veja a demonstração do JSFiddle fornecida por Chris Jordan em outra resposta aqui.

#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
fonte
324
isso funciona muito bem! exceto que existe um atraso no início, porque ele inicia para a altura máxima, que inicialmente é muito alta..hmm, acho que isso é um pouco chato
vsync
175
+1 Ótima solução! A velocidade da transição é calculada é calculada como o tempo que você especificar para fazer a transição para o valor da altura máxima ... mas, como a altura será menor que a altura máxima, a transição para a altura real ocorrerá mais rapidamente (geralmente significativamente) do que a altura. hora especificada.
kingjeffrey
50
Observe que isso pode causar uma transição feia que termina quando você precisa usar valores muito maiores que o valor calculado real. Eu notei isso ao tentar fazer uma div crescer de 0 a altura do conteúdo que varia muito devido a diferentes tamanhos de tela (2 linhas no meu monitor 2560x1440 vs> 10 linhas em um smartphone). Por isso acabei indo com js.
Jpeltoniemi
44
Solução muito feia, pois cria um atraso em uma direção, mas não na outra.
Tim
84
Esta é uma solução bastante preguiçosa. Estou assumindo que o OP deseja usar height: auto porque a altura expandida do contêiner é um tanto imprevisível. Esta solução causará um atraso antes que a animação se torne visível. Além disso, a duração visível da animação será imprevisível. Você obterá resultados muito mais previsíveis (e provavelmente mais suaves) calculando a altura combinada de cada um dos nós filhos dos contêineres e depois ajustando-os para um valor exato da altura.
Shawn Whinnery
314

Você deve usar scaleY.

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

Criei uma versão prefixada pelo fornecedor do código acima no jsfiddle e alterei seu jsfiddle para usar scaleY em vez de height.

dotnetCarpenter
fonte
6
Estou votando positivamente nesta resposta porque ela funciona bem em navegadores com capacidade de transição para css3, mas deve-se notar que isso não funcionará no IE <9 e não será animado (apenas saltará) no IE <10
Hailwood
215
Esse método atinge apenas parcialmente o efeito desejado, mas na verdade não remove o espaço. A caixa transformada age como um elemento relativamente posicionado - o espaço é ocupado, independentemente de como é dimensionado. Confira este jsFiddle, que pega o seu primeiro e adiciona apenas um texto falso na parte inferior. Observe como o texto abaixo não se move quando a altura da caixa é dimensionada para zero.
animuson
9
Agora ele faz: jsfiddle.net/gNDX3/1 Basicamente, você precisa estilizar seus elementos de acordo com o que você precisa. Não há marcador de prata ou widget como comportamento em CSS / HTML.
DotnetCarpenter
70
Enquanto eu aplaudo alguém que tenta abordagens diferentes, o efeito e as complicações do mundo real que essa solução traz é muito pior do que o já horrível hack de altura máxima. Por favor não use.
mystrdat
2
@SquareCat tudo bem, tudo bem. Aqui está um que é mais gaveta como: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter
202

No momento, não é possível animar em altura quando uma das alturas envolvidas é auto, você deve definir duas alturas explícitas.

robertc
fonte
12
Geralmente, existem várias maneiras de resolver um problema e nem todas são apropriadas ou possíveis. Não sei por que @JedidiahHurt e Ryan Ore estão tentando limitar possíveis respostas em um site de perguntas e respostas. Especialmente considerando que esta resposta foi aqui primeiro. Duplamente, já que a resposta aceita é uma solução alternativa.
Hooray estou ajudando
5
A resposta do @jake não é elegante nem correta. A resposta vinculada aqui é muito melhor. Funciona corretamente e lida com todos os casos de tamanho - nem se pode dizer a resposta aceita.
GraphicsMuncher
5
Sim: simthis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy
4
Isso está planejado para ser de alguma forma corrigido? Quer dizer, isso é bastante natural esperar para ser capaz de transição de 0 a altura para auto...
Augustin Riedinger
6
@Meliodas "não / você não pode" é uma resposta válida e aceitável para perguntas no Stack Overflow.
TylerH
122

A solução que eu sempre usei foi a primeira fora fade, então encolher os font-size, paddinge marginvalores. Não parece o mesmo que uma limpeza, mas funciona sem uma estática heightou max-height.

Exemplo de trabalho:

/* 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
fonte
Trabalhou ordenadamente para mim. Não é exatamente como o jQuery slideUp / Down. Você pode ver a diferença apenas com CPUs fracas, como computadores antigos ou móveis, e abrir e fechar muitas delas ao mesmo tempo.
Cruz Nunez
3
Se você tiver botões ou outros elementos que não sejam de texto, acabará com espaços em branco indesejados enquanto não estiver pairando.
Dennis W
3
Você pode refinar ainda mais, selecionando todos os elementos filho *e aplicando outras alterações. Os botões deveriam ter sido afetados pelo meu código acima, no entanto, se tivessem o estilo correto em.
Steven Vachon
1
No meu caso, eu também precisava adicionar line-height: 0;eborder-width: 0;
Andrey Shatilov 14/10
1
Você não precisa ajustar line-heightse evitar corretamente unidades no valor: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon
93

Sei que essa é a trigésima terceira resposta a essa pergunta, mas acho que vale a pena, então aqui vai. Esta é uma solução somente CSS com as seguintes propriedades:

  • Não há atraso no início, e a transição não para mais cedo. Nas duas direções (expansão e recolhimento), se você especificar uma duração de transição de 300ms no seu CSS, a transição levará 300ms, ponto final.
  • Ele está fazendo a transição da altura real (diferente transform: scaleY(0)), por isso faz a coisa certa se houver conteúdo após o elemento recolhível.
  • Enquanto (como em outras soluções), há são números mágicos (como "pegar um comprimento que é maior do que sua caixa está sempre vai ser"), não é fatal se suas extremidades suposição até estar errado. A transição pode não parecer incrível nesse caso, mas antes e depois da transição, isso não é um problema: No estado expandido ( height: auto), todo o conteúdo sempre tem a altura correta (diferente de, por exemplo, se você escolher uma max-heightque seja muito baixo). E no estado recolhido, a altura é zero como deveria.

Demo

Aqui está uma demonstração com três elementos recolhíveis, com diferentes alturas, que usam o mesmo CSS. Você pode clicar em "página inteira" após clicar em "executar trecho". Observe que o JavaScript apenas alterna a collapsedclasse CSS, não há medição envolvida. (Você pode fazer esta demonstração exata sem qualquer JavaScript usando uma caixa de seleção ou :target). Observe também que a parte do CSS responsável pela transição é bastante curta e o HTML requer apenas um único elemento adicional do wrapper.

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

Como funciona?

De fato, existem duas transições envolvidas para que isso aconteça. Um deles faz a transição margin-bottomde 0px (no estado expandido) para -2000pxno estado recolhido (semelhante a esta resposta ). O 2000 aqui é o primeiro número mágico, é baseado na suposição de que sua caixa não será maior que isso (2000 pixels parece uma escolha razoável).

Usando o margin-bottom transição sozinha por si só tem dois problemas:

  • Se você realmente tiver uma caixa com mais de 2000 pixels, uma margin-bottom: -2000px não ocultará tudo - haverá coisas visíveis mesmo no caso recolhido. Esta é uma pequena correção que faremos mais tarde.
  • Se a caixa real tiver, digamos, 1000 pixels de altura e sua transição tiver 300 ms, a transição visível já estará concluída após cerca de 150 ms (ou, na direção oposta, inicia 150 ms tarde).

A correção desse segundo problema é onde entra a segunda transição, e essa transição visa conceitualmente a altura mínima do wrapper ("conceitualmente" porque não estamos realmente usando a min-heightpropriedade para isso; mais sobre isso posteriormente).

Aqui está uma animação que mostra como a combinação da transição da margem inferior com a transição mínima da altura, ambas de igual duração, nos fornece uma transição combinada da altura total para a altura zero que tem a mesma duração.

animação como descrito acima

A barra esquerda mostra como a margem inferior negativa empurra a parte inferior para cima, reduzindo a altura visível. A barra do meio mostra como a altura mínima garante que, no caso de colapso, a transição não termine mais cedo e, no caso de expansão, a transição não comece tarde. A barra direita mostra como a combinação dos dois faz com que a caixa faça a transição da altura total para a altura zero na quantidade correta de tempo.

Para minha demonstração, estabeleci 50px como o valor mínimo de altura superior. Este é o segundo número mágico e deve ser menor do que a altura da caixa jamais seria. 50px parece razoável também; parece improvável que você queira muitas vezes tornar um elemento dobrável que não tem nem 50 pixels de altura.

Como você pode ver na animação, a transição resultante é contínua, mas não diferenciável - no momento em que a altura mínima é igual à altura total ajustada pela margem inferior, ocorre uma mudança repentina na velocidade. Isso é muito perceptível na animação porque usa uma função de tempo linear para ambas as transições e porque toda a transição é muito lenta. No caso real (minha demonstração no topo), a transição leva apenas 300ms e a transição da margem inferior não é linear. Eu brinquei com muitas funções de tempo diferentes para ambas as transições, e as que eu acabei achando que funcionaram melhor na mais ampla variedade de casos.

Restam dois problemas para corrigir:

  1. o ponto acima, onde caixas com mais de 2000 pixels de altura não estão completamente ocultas no estado recolhido,
  2. e o problema inverso, onde, no caso não oculto, caixas com menos de 50 pixels de altura são muito altas, mesmo quando a transição não está em execução, porque a altura mínima as mantém em 50 pixels.

Resolvemos o primeiro problema, atribuindo ao elemento contêiner a max-height: 0no caso recolhido, com uma 0s 0.3stransição. Isso significa que não é realmente uma transição, mas max-heighté aplicada com um atraso; só se aplica quando a transição termina. Para que isso funcione corretamente, também precisamos escolher um numérico max-heightpara o estado oposto, sem recolhimento . Mas, diferentemente do caso de 2000 px, onde escolher um número muito grande afeta a qualidade da transição, nesse caso, isso realmente não importa. Assim, podemos simplesmente escolher um número que é tão alto que sabemos que nenhuma altura chegará perto disso. Eu escolhi um milhão de pixels. Se você acha que precisa oferecer suporte a conteúdo com mais de um milhão de pixels de altura, 1) desculpe-me e 2) apenas adicione alguns zeros.

O segundo problema é a razão pela qual não estamos realmente usando min-heighta transição de altura mínima. Em vez disso, há um ::afterpseudoelemento no contêiner heightque transita de 50px para zero. Isso tem o mesmo efeito que min-height: Não permitirá que o contêiner encolha abaixo da altura que o pseudoelemento tenha atualmente. Mas como estamos usando height, não min-height, agora podemos usar max-height(mais uma vez aplicado com atraso) para definir a altura real do pseudo-elemento como zero quando a transição terminar, garantindo que, pelo menos fora da transição, até mesmo pequenos elementos tenham o altura correta. Por min-heightser mais forte que max-heightisso não funcionaria se usássemos o contêiner em min-heightvez do pseudo-elementoheight . Assim como omax-height no parágrafo anterior, isso max-heighttambém precisa de um valor para o extremo oposto da transição. Mas, neste caso, podemos simplesmente escolher os 50px.

Testado no Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (exceto por um problema de layout da caixa flexível na minha demonstração que não incomodava a depuração) e Safari (Mac, iOS ) Falando em flexbox, deve ser possível fazer isso funcionar sem usar flexbox; na verdade, acho que você poderia fazer quase tudo funcionar no IE7 - exceto pelo fato de não ter transições CSS, tornando-o um exercício inútil.

balpha
fonte
36
Desejo que você reduza (simplifique) sua solução ou pelo menos o exemplo de código - parece que 90% do exemplo de código não é relevante para sua resposta.
Gil Epshtain 30/07/2018
Existe uma maneira de fazer essas transições também funcionarem se a collapsible-wrapperposição for absoluta. O overflow: hiddenpai é necessário para a transição, mas interfere nos elementos absolutamente posicionados.
Pmkro #
1
@pmkro Sim, eu tive esse problema também. Eu o resolvi usando um em clip-path: polygon(...)vez de overflow: hidden, porque, ao contrário do último, é possível fazer a transição do primeiro. Como substituto para navegadores mais antigos, usei uma transformação de escala adicional. Veja o comentário na parte superior do github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha
2
Agradável. Isso funciona muito bem. Deve ser a resposta aceita
Henry Ing-Simmons
84

Você pode, com um pouco de brincadeira não-semântica. Minha abordagem usual é animar a altura de uma DIV externa que tenha um único filho, que é uma DIV sem estilo usada apenas para medir a altura do conteúdo.

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>

Alguém gostaria de poder simplesmente dispensar o .measuringWrappere apenas definir a altura do DIV para automático e fazer com que o animar, mas isso não parece funcionar (a altura é definida, mas não ocorre animação).

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>

Minha interpretação é que é necessária uma altura explícita para que a animação seja executada. Você não pode obter uma animação em altura quando a altura (a altura inicial ou final) é auto.

jhurshman
fonte
10
Como isso depende de javascript, você também pode adicionar facilmente o measureWrapper usando javascript também!
precisa saber é o seguinte
18
Você pode fazer isso sem o invólucro. Apenas: function growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
user1742529
8
Veja isto ... fale sobre uma resposta simples jsfiddle.net/n5XfG/2596 ... Sua resposta é incrivelmente complexa sem uma boa razão. Não há necessidade max-height, não há necessidade autoe, especialmente, não há necessidade de Javascript! Apenas duas declarações simples
VIDesignz 02/08/2015
12
@VIDesignz Você anima sobre a margem: 100%. Isso é 100% da largura do elemento, não da sua altura! Isso significa que, se você tiver um elemento com uma altura maior que sua largura, isso não o ocultará. Além disso, a velocidade real da animação é totalmente diferente daquela definida.
Timo Türschmann 15/04/16
3
Fiquei empolgado com a resposta de VIDesignz até ler mais sobre o comentário de Timo. Sim, margin-top(e margin-bottom) são relativos à largura quando definidos em porcentagens, sim, isso é estúpido, mas também explica por que os tempos de animação de abertura e fechamento são completamente diferentes - é a mesma coisa que você vê na resposta de melhor classificação, especificando um disco rígido. altura máxima codificada. Por que isso é tão difícil de fazer certo?
Coderer
52

Uma solução visual para animar a altura usando transições CSS3 é animar o preenchimento.

Você não obtém o efeito de limpeza total, mas brincar com os valores de duração da transição e preenchimento deve aproximá-lo o suficiente. Se você não deseja definir explicitamente altura / altura máxima, é isso que você está procurando.

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/ (extraído de stephband's acima do jsFiddle)

Catarse
fonte
3
Eu acho que essa é a melhor resposta. Essa técnica pode se estender ao ajuste do estofamento das crianças também. No meu caso, eu pude combiná-lo com transições explícitas de altura em alguns conteúdos sendo revelados, e o efeito total é muito mais bem-sucedido do que a solução alternativa de altura máxima, o que é bom para a revelação, mas apresenta um momento estranho de atraso na ocultação. Eu não animaria mais do que introduzir um atraso sem sentido que faz com que meu aplicativo pareça não responder. Estou surpresa que muitas pessoas pensam que isso é aceitável.
Ponto-
17
Só que aqui você não está animando a altura. Você está animando o preenchimento ... ele pode desaparecer muito bem porque pode animar do estado atual até 0, mas se você observar atentamente quando ele se expande, ele se abre com o texto e o preenchimento é animado apenas .. porque não sabe como animar de 0 a automático .... precisa de um intervalo numérico ... é assim que a interpolação funciona.
Rob R
Esta é uma boa solução alternativa que não é hacky. Não é a melhor resposta para essa pergunta, mas é uma alternativa que funcionou para mim. Às vezes você apenas precisa o efeito de alguma animação altura, não a animação completa :)
Ivan Durst
Essa solução também falha ao usar um atraso de transição.
Michel Joanisse 5/05
Concordo também que esta solução é uma solução alternativa limpa que fornece transições bastante fluidas se a animação for rápida o suficiente (no meu caso, para um acordeão com transições de 0,1s, é perfeito!). Porém, ele não é compatível com muitos aplicativos, mas os outros também não respondem a essa pergunta!
Remi D
46

Minha solução alternativa é fazer a transição da altura máxima para a altura exata do conteúdo para uma boa animação suave e, em seguida, usar um retorno de chamada transiçãoEnd para definir a altura máxima para 9999px para que o conteúdo possa ser redimensionado livremente.

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>

Adão
fonte
14
@ Derija93 Isso está usando animações antigas simples de tempo limite do javascript. O desafio é usar transições CSS3.
Adam
3
Bem, sim. Mas por que você usaria "transições CSS3" se, no seu exemplo, já usa jQuery, uma biblioteca que fornece muito código "escreva menos, faça mais"? Queria ressaltar que sua versão pode parecer muito melhor, embora mais complicada, se NÃO estiver usando o jQuery, para que possa ser executada em praticamente qualquer site. Acho que escrevi isso muito errado. Desculpa aí. ;)
Kiruse
27
@ Derija93 Talvez porque as transições CSS3 tenham (de acordo com todas as fontes que encontro) um desempenho muito melhor do que as animações jQuery. (Na verdade importa imensamente para o meu caso de uso corrente, o que me trouxe aqui.)
Mark Amery
4
@ Derija93 'Causa As animações Javascript rodam lentamente em comparação com as transições CSS3 bro, e nas animações jQuery você precisa se preocupar com o loop de animação. Anime algo ao clicar, clique rapidamente e observe as animações se repetirem. Isso é tratado com CSS3.
AlbertEngelB
2
@ Dropped.on.Caprica Isso é um pouco antigo agora. Eu já disse que não entendi o conceito que ele estava tentando demonstrar. De qualquer forma, para animações simples, .stopfunciona o problema do loop de animação bastante complexo. Agora, quando você anima a altura e a cor, mas deseja interromper apenas a animação da altura, as coisas ficam um pouco mais complicadas ... Eu entendi o seu ponto.
Kiruse
43

A resposta aceita funciona na maioria dos casos, mas não funciona bem quando você divpode variar bastante de altura - a velocidade da animação não depende da altura real do conteúdo e pode parecer instável.

Você ainda pode executar a animação real com CSS, mas precisa usar o JavaScript para calcular a altura dos itens, em vez de tentar usar auto. Não é necessário jQuery, embora você precise modificá-lo um pouco, se quiser compatibilidade (funciona na versão mais recente do 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
fonte
Isso pode ser útil, mas não sei dizer porque não sei usá-lo sem um exemplo melhor.
Ivan Durst
1
Você apenas passa qualquer elemento DOM (por exemplo, de document.getElementById('mydiv')ou $('#mydiv').get()), e a função alterna entre ocultar / mostrar. Se você configurar transições CSS, o elemento será animado automaticamente.
Oleg Vaskevich
Eu adicionei um trecho. Isso tem a vantagem de funcionar bem para conteúdo de tamanho dinâmico.
Oleg Vaskevich
3
-1, já que este não está "usando CSS". O ponto da questão é uma solução sem JS. Acho que a resposta "correta", isso não é um hack, seria isso, porém ...
dudewad
16
Downvote para usar JS - a sério? Metade das respostas aqui usam JS, para que seu voto negativo não pareça justo ou necessário. Além disso, esta resposta ainda está usando CSS para executar a animação real, que IMO era o ponto do OP. A única coisa para a qual o JS é usado é calcular a altura real, pois é impossível fazer isso com CSS puro (e a configuração min-heightcomo na resposta esperada causa problemas na velocidade da animação quando o tamanho da lista pode variar bastante).
Oleg Vaskevich
30

Houve pouca menção à Element.scrollHeightpropriedade que pode ser útil aqui e ainda pode ser usada com uma transição CSS pura. A propriedade sempre contém a altura "completa" de um elemento, independentemente de como e como seu conteúdo excede como resultado da altura recolhida (por exemplo height: 0).

Como tal, para um elemento height: 0(efetivamente totalmente recolhido), sua altura "normal" ou "total" ainda está prontamente disponível através de seu scrollHeightvalor (invariavelmente um comprimento de pixel ).

Para esse elemento, supondo que ele já tenha a transição configurada como por exemplo (usando ulconforme a pergunta original):

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

Podemos acionar a "expansão" animada desejada de altura, usando apenas CSS, com algo como o seguinte (assumindo aqui que a ulvariável se refere à lista):

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

É isso aí. Se você precisar recolher a lista, uma das duas instruções a seguir fará:

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

Meu caso de uso específico girava em torno de listas animadas de comprimentos desconhecidos e muitas vezes consideráveis; portanto, não me sentia confortável em optar por uma especificação "suficientemente grande" heightou arbitrária max-heighte por arriscar conteúdo de corte ou conteúdo que você precisa rolar de repente (se overflow: auto, por exemplo) . Além disso, a flexibilização e o tempo são interrompidos com as max-heightsoluções baseadas, porque a altura usada pode atingir seu valor máximo muito antes do que seria necessário para max-heightalcançá-lo 9999px. E à medida que as resoluções de tela aumentam, os comprimentos de pixel 9999pxdeixam um gosto ruim na minha boca. Esta solução em particular resolve o problema de maneira elegante, na minha opinião.

Finalmente, esperamos que futuras revisões dos autores de endereços CSS precisem fazer esse tipo de coisa de maneira ainda mais elegante - revisite a noção de valores "computados" vs "usados" e "resolvidos" e considere se as transições devem ser aplicadas a computadores computados. valores, incluindo transições com widthe height(que atualmente recebem um tratamento especial).

amn
fonte
6
Você não encontrou nas outras respostas aqui, porque interagir com o DOM usando a Element.scrollHeightpropriedade requer JavaScript e, como a pergunta indica claramente, eles querem fazer isso sem JavaScript.
precisa saber é o seguinte
3
Existem muitas outras partes do CSS além dessas duas pseudo-classes que podem resolver esse problema, como mostram as respostas mais votadas na página 1. Definir um estilo em JavaScript não é "CSS puro", pois exige que o JS seja definido em primeiro lugar. Freqüentemente, quando uma pessoa solicita uma solução apenas de CSS, é porque ela não pode injetar JavaScript ou não tem permissão para seu projeto ou função atual.
TylerH
29

Use max-heightcom flexibilização e atraso de transição diferentes para cada estado.

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

Consultar exemplo: http://jsfiddle.net/0hnjehjc/1/

malihu
fonte
12
O problema aqui é que, para garantir que você tenha espaço suficiente em um ambiente dinâmico, é necessário usar alturas máximas ridículas como 999999px. Por outro lado, isso mudará sua animação, porque os quadros são calculados a partir desse valor. Então você pode acabar com a animação "atraso" em um sentido e um fim muito rápido na outra direção (corr: o tempo-funções)
Julian F. Weinert
29

Não há valores codificados.

Sem JavaScript.

Sem aproximações.

O truque é usar um arquivo oculto e duplicado div para fazer o navegador entender o que 100% significa.

Este método é adequado sempre que você puder duplicar o DOM do elemento que deseja animar.

.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
fonte
Bem feito - esta é uma solução válida para um problema que realmente não deveria existir. Animar a altura máxima é básica, a menos que a transição esteja definida como "linear", também é (obviamente) muito ruim para animar o conteúdo do CMS onde a altura é variável.
M_Willett
isso é inteligente, mas não me sinto confortável em duplicar elementos e conteúdo DOM para isso.
Andre Figueiredo
Você pode fazer isso sem um contêiner e com melhor desempenho animando transform: scaleY(1), pois essa transição não remove o espaço.
Fabian von Ellerts
21

Ao postar isso, já existem mais de 30 respostas, mas sinto que minha resposta melhora a respostaaceita por jake.

Eu não estava contente com o problema que surge do simples uso das max-heighttransições CSS3, pois, como muitos comentadores observaram, você deve definir seu max-heightvalor muito próximo da altura real ou sofrerá um atraso. Veja este JSFiddle para um exemplo desse problema.

Para contornar isso (ainda sem usar JavaScript), adicionei outro elemento HTML que faz a transição do transform: translateYvalor CSS.

Isso significa os dois max-heighte translateYé usado: max-heightpermite que o elemento empurre elementos abaixo dele, enquanto translateYfornece o efeito "instantâneo" que queremos. O problema com max-heightainda existe, mas seu efeito é reduzido. Isso significa que você pode definir uma altura muito maior para o seu max-heightvalor e se preocupar com isso menos.

O benefício geral é que, na transição de volta (no colapso), o usuário vê a translateYanimação imediatamente, portanto, não importa realmente quanto tempo max-heightleva.

Solução como Fiddle

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
fonte
Boa comparação @davidtaubmann, sua caneta ilustra muito bem as diferenças! Fui com translateYmais scaleporque eu não gostei de como scaledeu esse efeito de compressão.
Andrew Messier
Obrigado @ andrew-messier, excluí o comentário porque há um erro, o translateY não está sendo executado em codepen.io/davidtaubmann/pen/zKZvGP (porque não há criança para fazer a mudança, precisa de uma correção) ... mas neste podemos ver perfeitamente a comparação de ONLY MAX-HEIGHT e misturá-la com a escala (como indicado em outras respostas): codepen.io/davidtaubmann/pen/jrdPER . Isso tudo me ajudou muito com uma metodologia alvo estou usando
DavidTaubmann
18

Ok, então acho que encontrei uma resposta super simples ... não max-height, usa relativeposicionamento, trabalha com lielementos e é CSS puro. Eu não testei em nada além do Firefox, apesar de julgar pelo CSS, ele deve funcionar em todos os navegadores.

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
fonte
13
Isso funcionará até que a altura do elemento exceda sua largura. É a base de como as margens são calculadas usando porcentagens: elas são calculadas com base na largura do elemento. Portanto, se você tiver um elemento de 1000 px de largura, um elemento de 1100 px será muito grande para que esta solução funcione, o que significa que você terá que aumentar a margem superior negativa. Basicamente, é exatamente o mesmo problema que usar altura ou altura máxima.
Dudewad 19/08/2015
15

EDIT: Role para baixo para obter uma resposta atualizada
Eu estava fazendo uma lista suspensa e vi este Post ... muitas respostas diferentes, mas decido compartilhar minha lista suspensa também ... Não é perfeito, mas pelo menos usará apenas css para suspenso! Eu tenho usado transform: translateY (y) para transformar a lista na exibição ...
Você pode ver mais no teste
http://jsfiddle.net/BVEpc/4/ Coloquei
div por trás de cada li porque meu lista suspensa vem de cima e para mostrá-las corretamente, isso era necessário, meu código div é:

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

e passe o mouse é:

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

e como a altura de ul é definida como o conteúdo, ela pode superar o conteúdo do seu corpo, por isso fiz isso para ul:

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

e passe o mouse:

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

a segunda vez após a transição for atrasada e ela ficará oculta após minha lista suspensa ter sido animada ...
Espero que mais tarde alguém se beneficie dessa.

Edição: Eu simplesmente não posso acreditar ppl realmente usando este protótipo! este menu suspenso é apenas para um submenu e é tudo !! Atualizei um melhor que pode ter dois submenus para a direção ltr e rtl com o suporte do IE 8.
Fiddle for LTR
Fiddle for RTL
espero que alguém ache isso útil no futuro.

Sijav
fonte
11

Você pode fazer a transição de height: 0 para height: auto, desde que você também forneça altura mínima e altura máxima.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
Stuart Badminton
fonte
@Stuart Badminton, você pode dar um exemplo prático? Eu coloquei isso junto, mas ele não parece em qualquer lugar de trabalho :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin
5
O problema está na sua regra de transição. para fazê-lo funcionar, você também deve aplicar a transição para a altura mínima e máxima. transição: .5s altura linear, .5s min-altura linear, .5s max-altura linear
Stuart Badminton
2
aqui está o que eu tenho, mais tarde, como você disse, necessária para animar max-height: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin
11
Claro, há um problema com isso. O tempo necessário para animar a altura máxima não é o tempo necessário para fazer a transição para a altura automática da caixa: ele chega à altura: primeiro automático, antes que a altura máxima termine a animação. Da mesma forma, ao fazer a transição para 0, a transição não começa imediatamente, pois a altura máxima começa em uma altura maior que a altura: auto. A transição para max-height: 1000px deixa isso claro: jsfiddle.net/n5XfG/11
stephband
Você está certo, em testes adicionais, notei isso. Não sei se uma altura total: 0 a altura: auto será implementada, mas o uso de uma altura máxima em algumas situações resolve uma situação impossível de outra maneira, usando apenas CSS.
Stuart Badminton
10

Solução Flexbox

Prós:

  • simples
  • sem JS
  • transição suave

Contras:

  • O elemento precisa ser colocado em um contêiner flexível de altura fixa

A maneira como funciona é sempre ter base flexível: auto no elemento com conteúdo e, em vez disso, fazer a transição flex-grow e flex-shrink.

Edit: JS Fiddle aprimorado, inspirado na interface do 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
fonte
7

Expandindo a resposta de @ jake, a transição alcançará o valor de altura máxima, causando uma animação extremamente rápida - se você definir as transições para ambos: pairar e desligar, poderá controlar um pouco mais a velocidade louca.

Portanto, o li: hover é quando o mouse entra no estado e, em seguida, a transição na propriedade não pairada será deixada pelo mouse.

Espero que isso ajude.

por exemplo:

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

Aqui está um violino: http://jsfiddle.net/BukwJ/

jamie
fonte
1
Eu não sei por que isso tem um -1. Na verdade, acho que essa é uma tentativa melhor do que algumas pessoas adicionando cargas de javascript. Não é uma solução perfeita, mas com alguns ajustes ele faz um bom trabalho, achei que funcionava com o valor de retorno. transição: todo o bezier cúbico de 500 ms (0,000, 1,225, 0,085, 0,385); Isso foi baseado em uma transição 2s na abertura do código acima para voltar ao estado inicial. Obrigado ... Isso me ajudou melhor do que todas as opções acima. +1 Eu usei este site para criar o bezier: matthewlein.com/ceaser
gcoulby:
OK, levou um pouco mais de ajustes. transição: todo o bezier cúbico de 500 ms (0,000, 1,390, 0,000, 0,635); Devo especificar que minha transição vai de 5% a 100% (mas, na realidade, o valor final é de cerca de 28%), portanto depende do projeto.
precisa saber é o seguinte
Absolutamente, não é o ideal, mas é uma solução rápida, se você souber sobre qual será a altura média.
Jamie
7

Eu acho que criei uma solução realmente sólida

ESTÁ BEM! Eu sei que esse problema é tão antigo quanto a Internet, mas acho que tenho uma solução que me transformei em um plug - in chamado transição de mutantes . Minha solução define os style=""atributos para elementos rastreados sempre que houver uma alteração no DOM. o resultado final é que você pode usar um bom ole CSS para suas transições e não usar correções de hackers ou javascript especial. A única coisa que você precisa fazer é definir o que deseja rastrear no elemento em questão usando data-mutant-attributes="X".

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

É isso aí! Esta solução usa MutationObserver para acompanhar as alterações no DOM. Por isso, você não precisa realmente configurar nada ou usar o javascript para animar manualmente as coisas. As alterações são rastreadas automaticamente. No entanto, como ele usa o MutationObserver, isso fará a transição apenas no IE11 +.

Fiddles!

JonTroncoso
fonte
12
Normalmente, quando alguém pergunta como fazer algo "sem JavaScript", significa que a solução deve funcionar em navegadores com o JavaScript desativado. Isso não significa "sem escrever meus próprios scripts". Assim, dizer que seu plug-in pode ser usado sem usar JavaScript é enganoso, na melhor das hipóteses - considerando que é um plug-in JavaScript.
BoltClock
Devo esclarecer: quando digo que você não precisa usar nenhum javascript especial, quero dizer que você não precisa escrever nenhum javascript. inclua a biblioteca JS e especifique quais atributos você deseja assistir no HTML. Você não precisa usar CSS de altura fixa ou descobrir alguma coisa. apenas estilize e vá embora.
JonTroncoso
"(...) você realmente não precisa configurar nada ou usar o javascript para animar manualmente as coisas (...)", para usar o JavaScript por diversão
Roko C. Buljan
6

Aqui está uma maneira de fazer a transição de qualquer altura inicial, incluindo 0, para automática (tamanho total e flexível) sem exigir código de configuração fixa por nó ou qualquer código de usuário para inicializar: https://github.com/csuwildcat / transição-automática . Este é basicamente o Santo Graal para o que você quer, acredito -> http://codepen.io/csuwldcat/pen/kwsdF . Basta colocar o seguinte arquivo JS em sua página e tudo o que você precisa fazer depois é adicionar / remover um único atributo booleano -reveal="" - dos nós que deseja expandir e contratar.

Aqui está tudo o que você precisa fazer como usuário, depois de incluir o bloco de código encontrado abaixo do código de exemplo:

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

Aqui está o bloco de código a incluir na sua página; depois disso, é tudo molho:

Solte esse arquivo JS na sua página - tudo isso apenas funciona

/ * Código para altura: auto; transição * /

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

/ * Código para 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
fonte
9
Embora essa seja uma solução muito mais limpa e flexível do que as outras sugeridas, eu pessoalmente acredito que isso não deve ser algo que JS deva tocar. Não dizendo que você está errado, é apenas um suspiro.
mystrdat 18/11/2013
@mystrdat para ficar claro, porém, o JS é usado apenas para observar determinadas condições e adicionar alguns atributos / valores CSS temporários com base nos eventos / condições que ele está observando. A transição ainda é feita em CSS. Embora eu concorde, é triste que esse nível de configuração e manutenção da mão seja necessário para realmente satisfazer um caso de uso aparentemente simples! : /
csuwldcat
1
@KarolisRamanauskas não, isso não é um problema - o IE suporta os quadros-chave CSS e requestAnimationFrame. Eu estava usando clipcomo meu gatilho de propriedade fictícia e, por algum motivo, o IE parou de permitir que o clipe fosse animado. Mudei para a opacidade e tudo funciona novamente: codepen.io/csuwldcat/pen/FpzGa . Atualizei o código na resposta para corresponder.
precisa saber é o seguinte
1
@csuwldcat Solução muito boa, notei que o conteúdo aparece e desaparece por uma pequena fração, por que isso acontece? É evitável?
Beto Aveiga 19/08/14
12
Quero fazer uma votação positiva, mas em vez de responder à pergunta de como fazê-lo funcionar, você compartilhou um plug-in que escreveu para fazê-lo funcionar. Nós, os curiosos, temos que fazer engenharia reversa no seu plugin, o que não é muito divertido. Desejo que você atualize sua resposta para conter mais explicações sobre o que seu plug-in faz e por quê. Mude o código para ser mais explicativo. Por exemplo, você tem uma seção inteira de código que apenas grava CSS estático. Prefiro ver o CSS do que o código que o gera. Você pode deixar de fora as partes chatas, como repetir para todos os prefixos do navegador.
precisa saber é o seguinte
6

A resposta de Jake para animar a altura máxima é ótima, mas achei o atraso causado por definir uma grande altura máxima irritante.

Pode-se mover o conteúdo recolhível para uma div interna e calcular a altura máxima obtendo a altura da div interna (via JQuery, seria o 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');
});

Aqui está um link do jsfiddle: http://jsfiddle.net/pbatey/duZpT

Aqui está um jsfiddle com a quantidade mínima absoluta de código necessária: http://jsfiddle.net/8ncjjxh8/

pbatey
fonte
5

Sei que esse segmento está envelhecendo, mas está classificado em algumas pesquisas no Google, então acho que vale a pena atualizar.

Você também obtém / define a altura do elemento:

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

Você deve despejar esse Javascript imediatamente após a tag de fechamento do target_box em uma tag de script embutido.

Charley
fonte
document.getElementById ('target_box'). getBoundingClientRect (). height deve fazer o melhor.
altero
5

Eu fui capaz de fazer isso. Eu tenho um .childe um .parentdiv. A div criança se encaixa perfeitamente dentro da largura / altura dos pais com o absoluteposicionamento. Em seguida, animar a translatepropriedade para empurrar seu Yvalor para baixo 100%. Sua animação muito suave, sem falhas ou desvantagens como qualquer outra solução aqui.

Algo assim, pseudo código

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

Você deve especificar um heightno .parent, no px, %ou licença como auto. Essa div mascara a .childdiv quando ela desliza para baixo.

Josh Ribakoff
fonte
O exemplo de trabalho disso será muito apreciado.
Neurotransmissor
5

Eu postei uma resposta com algum JavaScript e obtive votos negativos, por isso fiquei irritado e tentei novamente, e o decifrei apenas com CSS!

Esta solução usa algumas 'técnicas':

  • padding-bottom:100%'hack' onde porcentagens são definidas em termos da largura atual do elemento. Mais informações sobre esta técnica.
  • embalagem por contração do flutuador (necessitando de uma div extra para aplicar o corte de limpeza do flutuador)
  • uso não semântico de https://caniuse.com/#feat=css-writing-mode e algumas transformações para desfazê-lo (isso permite o uso do preenchimento de preenchimento acima em um contexto vertical)

O resultado, porém, é que obtemos uma transição de desempenho usando apenas CSS e uma única função de transição para obter a transição sem problemas; o Santo Graal!

Claro, há uma desvantagem! Não sei como controlar a largura em que o conteúdo é cortado ( overflow:hidden); por causa do hack do fundo do preenchimento, a largura e a altura estão intimamente relacionadas. Pode haver uma maneira, então, voltarei a ela.

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
fonte
A outra desvantagem disso é que você não pode mover o cursor da div "hoverme" para a lista de itens para interagir com eles (ostensivamente o motivo da solicitação do OP), por isso é útil apenas como uma dica de ferramenta em vez de , digamos, um menu com submenus aninhados.
TylerH
Isso é puramente uma consequência da maneira como a pergunta foi formulada e não está relacionada à técnica. Você poderia mudar o gatilho para ser um input[type="checkbox"]:checked, em vez de :hoverse desejar (ou seja, você não quer usar JavaScript para adicionar quaisquer classes)
EoghanM
Quero dizer, independentemente de como a pergunta é formulada, se a implementação de uma solução é tão limitada, ela provavelmente deve ser mencionada na resposta dessa solução (ou preferencialmente contabilizada ). Você mencionou que esta solução é um 'santo graal', mas a div em transição nem pode ser colocada sobre o mouse ... não parece ser uma solução definitiva para mim.
TylerH
Oi @TylerH, desculpas, eu perdi o fato de que o gatilho na pergunta original envolve a lista expandida, portanto a lista deve permanecer aberta quando passar o mouse. Atualizei minha resposta para refletir isso. Como mencionei, no entanto, como é acionado não é tão pertinente, pois estou apresentando uma nova técnica que antes não era possível apenas em CSS (estou procurando uma solução há alguns anos).
EoghanM
4

Aqui está uma solução que acabei de usar em combinação com o jQuery. Isso funciona para a seguinte estrutura HTML:

<nav id="main-nav">
    <ul>
        <li>
            <a class="main-link" href="yourlink.html">Link</a>
            <ul>
                <li><a href="yourlink.html">Sub Link</a></li>
            </ul>
        </li>
    </ul>
</nav>

e a função:

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

Você pode usar uma função de clique para definir e remover a altura usando 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
fonte
isso não aparecem corretamente com a versão mais recente do jQuery
oliverbachmann
4

Estive recentemente fazendo a transição max-heightdos lielementos em vez da embalagemul .

O raciocínio é que o atraso para os pequenos max-heightsé muito menos perceptível (se em tudo) em relação ao grande max-heights, e eu também pode definir o meu max-heightvalor relativo ao font-sizedo liem vez de um número enorme arbitrária usando emsourems .

Se o tamanho da minha fonte for 1rem, definirei meu max-heightcomo algo como 3rem(para acomodar o texto quebrado). Você pode ver um exemplo aqui:

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

mindfullsilence
fonte
3

Não li tudo em detalhes, mas tive esse problema recentemente e fiz o seguinte:

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

Isso permite que você tenha uma div que, a princípio, mostre conteúdo com até 200px de altura e, ao passar o mouse, o tamanho se torna pelo menos tão alto quanto o conteúdo inteiro da div. O Div não se torna 3000px, mas 3000px é o limite que estou impondo. Certifique-se de fazer a transição no non: hover, caso contrário você poderá obter uma renderização estranha. Dessa maneira, o: hover é herdado do non: hover.

A transição não funciona do px para% ou para automático. Você precisa usar a mesma unidade de medida. Este trabalho é bom para mim. Usar HTML5 o torna perfeito ....

Lembre-se de que sempre há uma solução alternativa ...; )

Espero que alguém ache isso útil

work.stella84
fonte
3

A solução de altura máxima da Jake funciona bem, se o valor da altura máxima codificado fornecido não for muito maior que a altura real (porque, caso contrário, existem atrasos e problemas de tempo indesejáveis). Por outro lado, se o valor codificado permanentemente não for maior que a altura real, o elemento não abrirá completamente.

A solução somente CSS a seguir também requer um tamanho codificado que deve ser maior que a maioria dos tamanhos reais que ocorrem. No entanto, esta solução também funciona se, em algumas situações, o tamanho real for maior que o tamanho codificado. Nesse caso, a transição pode pular um pouco, mas nunca deixará um elemento parcialmente visível. Portanto, essa solução também pode ser usada para conteúdo desconhecido, por exemplo, de um banco de dados, onde você sabe que o conteúdo geralmente não é maior que x pixels, mas há exceções.

A idéia é usar um valor negativo para margem inferior (ou margem superior para uma animação ligeiramente diferente) e colocar o elemento de conteúdo em um elemento intermediário com estouro: oculto. A margem negativa do elemento de conteúdo reduz a altura do elemento do meio.

O código a seguir usa uma transição na margem inferior de -150px para 0px. Só isso funciona bem, desde que o elemento de conteúdo não seja superior a 150px. Além disso, ele usa uma transição na altura máxima para o elemento do meio de 0px a 100%. Por fim, isso oculta o elemento do meio se o elemento de conteúdo for superior a 150px. Para altura máxima, a transição é usada apenas para atrasar sua aplicação em um segundo ao fechar, não para um efeito visual suave (e, portanto, pode variar de 0px a 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>

O valor da margem inferior deve ser negativo e o mais próximo possível da altura real do elemento de conteúdo. Se (o valor do absoute) for maior, existem problemas de atraso e tempo semelhantes aos das soluções de altura máxima, que podem ser limitadas desde que o tamanho do código rígido não seja muito maior que o real. Se o valor absoluto para o fundo da margem for menor que a altura real, a transição pula um pouco. De qualquer forma, após a transição, o elemento de conteúdo é totalmente exibido ou totalmente removido.

Para obter mais detalhes, consulte minha postagem no blog http://www.taccgl.org/blog/css_transition_display.html#combined_height

Helmut Emmelmann
fonte
3

Você pode fazer isso criando uma animação reversa (recolhida) com o caminho do clipe.

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

Esta não é exatamente uma "solução" para o problema, mas mais uma solução alternativa. Funciona apenas como escrito com texto, mas pode ser alterado para funcionar com outros elementos, conforme necessário, tenho certeza.

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

Aqui está um exemplo: http://codepen.io/overthemike/pen/wzjRKa

Essencialmente, você define o tamanho da fonte como 0 e faz a transição para que, em vez da altura, altura máxima ou escalaY () etc., em um ritmo rápido o suficiente para que a altura se transforme no que você deseja. Atualmente, não é possível transformar a altura real com CSS para automático, mas transformar o conteúdo é, portanto, a transição do tamanho da fonte.

  • Nota - existe Javascript no codepen, mas seu único objetivo é adicionar / remover classes css ao clicar no acordeão. Isso pode ser feito com botões de opção ocultos, mas eu não estava focado nisso, apenas na transformação da altura.
Mike S.
fonte
1
Duplica efetivamente essa resposta , mas omite o efeito da opacidade.
SeldomNeedy 7/10/19
Acordado. Melhor abordagem.
Mike S.