¿Cómo puedo hacer la transición de altura: 0; a la altura: auto; usando CSS?

2136

Estoy tratando de hacer una <ul>diapositiva hacia abajo usando transiciones CSS.

El <ul>comienza a las height: 0;. Al pasar el mouse, la altura se establece en height:auto;. Sin embargo, esto hace que simplemente aparezca, no transición,

Si lo hago de height: 40px;a height: auto;, entonces se deslizará hacia arriba height: 0;y de repente saltará a la altura correcta.

¿De qué otra forma podría hacer esto sin 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>

Granizo
fuente
Creo que la height:auto/max-heightsolución solo funcionará si su área de expansión es mayor que la heightque desea restringir. Si tiene un max-heightde 300px, pero un menú desplegable de cuadro combinado, que puede regresar 50px, max-heightque no lo ayudará, 50pxes variable dependiendo de la cantidad de elementos, puede llegar a una situación imposible donde no puedo solucionarlo porque heightno es arreglado, height:autoera la solución, pero no puedo usar transiciones con esto.
Christopher Thomas
51
OP está intentando una solución css, no js, ​​de lo contrario podrían usar overflow y animar
Toni Leigh
55
@VIDesignz: Pero el div -100% margin-top interno recibe el ancho del envoltorio div, no la altura. Entonces, esta solución tiene el mismo tipo de problema que la solución de altura máxima. Además, cuando el ancho es menor que la altura del contenido, no todo el contenido está oculto por -100% de margen superior. Entonces esta es una solución incorrecta.
GregTom
1
Visite github.com/w3c/csswg-drafts/issues/626 para analizar la especificación y la implementación de una solución adecuada
Ben Creasy,

Respuestas:

2746

Uso max-heighten la transición y no height. Y establezca un valor en max-heightalgo más grande de lo que su caja obtendrá.

Vea la demostración de JSFiddle proporcionada por Chris Jordan en otra respuesta aquí.

#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
fuente
324
esto funciona muy bien! excepto que hay un retraso cuando comienza, porque comienza para la altura máxima que inicialmente es muy alta ... mmm, creo que esto es algo molesto
vsync
175
+1 ¡Gran solución! La velocidad de la transición se calcula como el tiempo que especifique para la transición al valor de altura máxima ... pero como la altura será menor que la altura máxima, la transición a la altura real ocurrirá más rápido (a menudo significativamente) que el tiempo especificado
kingjeffrey
50
Tenga en cuenta que esto puede causar una transición fea que finaliza cuando tiene que usar valores que son mucho más grandes que el valor calculado real. Me di cuenta de esto al intentar hacer que un div creciera de 0 a la altura del contenido que varía mucho debido a los diferentes tamaños de pantalla (2 líneas en mi monitor 2560x1440 frente a> 10 líneas en un teléfono inteligente). Por esto terminé yendo con js.
jpeltoniemi
44
Solución muy fea ya que crea un retraso en una dirección pero no en la otra.
Tim
84
Esta es una solución bastante perezosa. Supongo que OP quiere usar height: auto porque la altura expandida del contenedor es algo impredecible. Esta solución causará un retraso antes de que la animación se vuelva visible. Además, la duración visible de la animación será impredecible. Obtendrá resultados mucho más predecibles (y probablemente más suaves) calculando la altura combinada de cada uno de los nodos secundarios de los contenedores y luego reduciendo a un valor de altura exacto.
Shawn Whinnery
314

Deberías usar scaleY en su lugar.

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>

Hice una versión con prefijo de proveedor del código anterior en jsfiddle , y cambié su jsfiddle para usar scaleY en lugar de height.

dotnetCarpenter
fuente
66
Estoy votando esta respuesta porque funciona bien en navegadores con capacidad de transición css3, pero debe tenerse en cuenta que esto no funcionará en absoluto en IE <9 , y no estará animado (solo saltará) en IE <10
Hailwood
215
Este método solo logra parcialmente el efecto deseado, pero en realidad no elimina el espacio. La caja transformada actúa como un elemento relativamente posicionado: el espacio se ocupa sin importar cómo se escala. Echa un vistazo a este jsFiddle que toma el primero y solo agrega un texto falso en la parte inferior. Observe cómo el texto debajo no se mueve hacia arriba cuando la altura del cuadro se escala a cero.
animuson
99
Ahora sí: jsfiddle.net/gNDX3/1 Básicamente, necesita diseñar sus elementos de acuerdo con lo que necesita. No hay un comportamiento similar a una viñeta plateada o widget en CSS / HTML.
dotnetCarpenter
70
Si bien aplaudo a alguien que intenta diferentes enfoques, el efecto en el mundo real y las complicaciones que trae esta solución son mucho peores que el ya horrible truco de altura máxima. Por favor, no use.
mystrdat
2
@SquareCat está bien, está bien. Aquí hay uno que es más cajón como: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter
202

Actualmente no puede animar en altura cuando una de las alturas involucradas es auto, debe establecer dos alturas explícitas.

robertc
fuente
12
Por lo general, hay varias formas de resolver un problema, y ​​no todas son apropiadas o posibles. No sé por qué @JedidiahHurt y Ryan Ore están tratando de limitar las posibles respuestas en un sitio de preguntas y respuestas. Especialmente considerando que esta respuesta fue aquí primero. Doblemente, ya que la respuesta aceptada es una solución alternativa.
Hooray Im Helping
55
La respuesta de @jake no es elegante ni correcta. La respuesta vinculada aquí es mucho mejor. Funciona correctamente y maneja todos los casos de tamaño, ni se puede decir de la respuesta aceptada.
GraphicsMuncher
55
Sí: lo hacethis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy
44
¿Se planea solucionarlo de alguna manera? Quiero decir, es bastante natural esperar poder pasar de la altura 0 a auto...
Augustin Riedinger
66
@Meliodas "no / you don't" es una respuesta válida y aceptable a las preguntas sobre Stack Overflow.
TylerH
122

La solución que yo siempre he usado era la primera desaparición gradual, a continuación, reducir el tamaño de font-size, paddingy marginvalores. No se ve igual que una toallita, pero funciona sin estática heighto max-height.

Ejemplo de trabajo:

/* 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
fuente
Trabajó perfectamente para mí. No es como el jQuery slideUp / Down. Puede ver una diferencia solo con CPU débiles, como computadoras viejas o móviles, y al abrir y cerrar muchas de ellas al mismo tiempo.
Cruz Núñez
3
Si tiene botones o cualquier otro elemento que no sea de texto, terminará con espacios en blanco no deseados sin desplazarse.
Dennis W
3
Puede refinarlo aún más seleccionando todos los elementos secundarios *y aplicando otros cambios. Sin embargo, los botones deberían haberse visto afectados por mi código anterior, si se diseñaron correctamente em.
Steven Vachon
1
En mi caso, también necesitaba agregar line-height: 0;yborder-width: 0;
Andrey Shatilov
1
No debería necesitar ajustar line-heightsi evita correctamente las unidades en el valor: developer.mozilla.org/en-US/docs/Web/CSS/…
Steven Vachon
93

Sé que esta es la respuesta de treinta y tantos a esta pregunta, pero creo que vale la pena, así que aquí va. Esta es una solución solo CSS con las siguientes propiedades:

  • No hay demora al principio, y la transición no se detiene temprano. En ambas direcciones (expandir y contraer), si especifica una duración de transición de 300 ms en su CSS, la transición dura 300 ms, punto.
  • Está transform: scaleY(0)haciendo la transición de la altura real (a diferencia de ), por lo que hace lo correcto si hay contenido después del elemento plegable.
  • Mientras que (al igual que en otras soluciones) no son números mágicos (como "escoger una longitud que es mayor que su caja es cada vez va a ser"), no es fatal si sus extremos asunción siendo mal. La transición puede no parecer sorprendente en ese caso, pero antes y después de la transición, esto no es un problema: en el estado expandido ( height: auto), todo el contenido siempre tiene la altura correcta (a diferencia, por ejemplo, si elige un max-heightque resulta ser demasiado baja). Y en el estado colapsado, la altura es cero como debería.

Manifestación

Aquí hay una demostración con tres elementos plegables, todos de diferentes alturas, que usan el mismo CSS. Es posible que desee hacer clic en "página completa" después de hacer clic en "ejecutar fragmento". Tenga en cuenta que JavaScript solo alterna la collapsedclase CSS, no hay mediciones involucradas. (Puede hacer esta demostración exacta sin ningún tipo de JavaScript utilizando una casilla de verificación o :target). También tenga en cuenta que la parte del CSS que es responsable de la transición es bastante corta, y el HTML solo requiere un único elemento contenedor adicional.

$(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 hecho, hay dos transiciones involucradas en hacer que esto suceda. Uno de ellos hace la transición margin-bottomde 0px (en el estado expandido) al -2000pxestado colapsado (similar a esta respuesta ). El 2000 aquí es el primer número mágico, se basa en el supuesto de que su caja no será más alta que esta (2000 píxeles parece una opción razonable).

Usar la margin-bottomtransición solo por sí solo tiene dos problemas:

  • Si realmente tiene un cuadro que tiene más de 2000 píxeles, entonces margin-bottom: -2000pxno lo ocultará todo, habrá cosas visibles incluso en el caso colapsado. Esta es una solución menor que haremos más adelante.
  • Si el cuadro real tiene, digamos, 1000 píxeles de alto, y su transición es de 300 ms de largo, entonces la transición visible ya ha terminado después de aproximadamente 150 ms (o, en la dirección opuesta, comienza 150 ms tarde).

La solución de este segundo problema es donde entra la segunda transición, y esta transición se dirige conceptualmente a la altura mínima del contenedor ("conceptualmente" porque en realidad no estamos usando la min-heightpropiedad para esto; más sobre eso más adelante).

Aquí hay una animación que muestra cómo la combinación de la transición del margen inferior con la transición de altura mínima, ambas de igual duración, nos proporciona una transición combinada de altura completa a altura cero que tiene la misma duración.

animación como se describe arriba

La barra izquierda muestra cómo el margen inferior negativo empuja el fondo hacia arriba, reduciendo la altura visible. La barra del medio muestra cómo la altura mínima asegura que en el caso de colapso, la transición no termine temprano, y en el caso de expansión, la transición no comienza tarde. La barra derecha muestra cómo la combinación de los dos hace que la caja pase de la altura completa a la altura cero en la cantidad de tiempo correcta.

Para mi demo, me decidí por 50px como el valor de altura mínima superior. Este es el segundo número mágico, y debería ser más bajo que la altura de la caja. 50 px parece razonable también; parece poco probable que a menudo quiera hacer un elemento plegable que no tenga ni 50 píxeles de altura en primer lugar.

Como puede ver en la animación, la transición resultante es continua, pero no es diferenciable: en el momento en que la altura mínima es igual a la altura completa ajustada por el margen inferior, hay un cambio repentino en la velocidad. Esto es muy notable en la animación porque usa una función de temporización lineal para ambas transiciones, y porque toda la transición es muy lenta. En el caso real (mi demo en la parte superior), la transición solo toma 300 ms, y la transición del margen inferior no es lineal. He jugado con muchas funciones de temporización diferentes para ambas transiciones, y las que terminé sentían que funcionaban mejor para la más amplia variedad de casos.

Quedan dos problemas por solucionar:

  1. el punto de arriba, donde los cuadros de más de 2000 píxeles de altura no están completamente ocultos en el estado colapsado,
  2. y el problema inverso, donde en el caso no oculto, los cuadros de menos de 50 píxeles de altura son demasiado altos incluso cuando la transición no se está ejecutando, porque la altura mínima los mantiene en 50 píxeles.

Resolvemos el primer problema dando al elemento contenedor a max-height: 0en el caso colapsado, con una 0s 0.3stransición. Esto significa que no es realmente una transición, sino que max-heightse aplica con retraso; solo se aplica una vez que finaliza la transición. Para que esto funcione correctamente, también debemos elegir un valor numérico max-heightpara el estado opuesto, no colapsado. Pero a diferencia del caso de 2000px, donde elegir un número demasiado grande afecta la calidad de la transición, en este caso, realmente no importa. Entonces, podemos elegir un número que sea tan alto que sepamos que ninguna altura se acercará a esto. Elegí un millón de píxeles. Si cree que puede necesitar contenido de una altura de más de un millón de píxeles, entonces 1) lo siento, y 2) simplemente agregue un par de ceros.

El segundo problema es la razón por la que en realidad no estamos utilizando min-heightla transición de altura mínima. En cambio, hay un ::afterpseudo-elemento en el contenedor con una heighttransición de 50 px a cero. Esto tiene el mismo efecto que un min-height: No permitirá que el contenedor se encoja por debajo de la altura que tenga el pseudo-elemento actualmente. Pero debido a que estamos usando height, no min-height, ahora podemos usar max-height(una vez más aplicado con un retraso) para establecer la altura real del pseudo-elemento a cero una vez que finaliza la transición, asegurando que al menos fuera de la transición, incluso los elementos pequeños tengan el altura correcta Porque min-heightes más fuerte que max-height, esto no funcionaría si usáramos los contenedores en min-heightlugar de los pseudoelementosheight. Al igual que max-heighten el párrafo anterior, esto max-heighttambién necesita un valor para el extremo opuesto de la transición. Pero en este caso, podemos elegir el 50px.

Probado en Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (excepto por un problema de diseño de flexbox con mi demo que no me molestó en depurar) y Safari (Mac, iOS ) Hablando de flexbox, debería ser posible hacer que esto funcione sin usar ningún flexbox; de hecho, creo que podría hacer que casi todo funcione en IE7, excepto el hecho de que no tendrá transiciones CSS, lo que lo convierte en un ejercicio bastante inútil.

balpha
fuente
36
Desearía que pudieras acortar (simplificar) tu solución, o el ejemplo del código al menos, parece que el 90% del ejemplo del código no es relevante para tu respuesta.
Gil Epshtain
¿Hay alguna manera de hacer que estas transiciones también funcionen si collapsible-wrappertiene una posición absoluta? Se overflow: hiddenrequiere el en el padre para la transición, pero interfiere con elementos posicionados absolutamente.
pmkro
1
@pmkro Sí, también tuve ese problema. Lo resolví usando un en clip-path: polygon(...)lugar de overflow: hidden, porque a diferencia de este último, puede hacer la transición del primero. Como alternativa para los navegadores más antiguos, utilicé una transformación de escala adicional. Vea el comentario en la parte superior de github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha
2
Agradable. Esto funciona muy bien Debería ser la respuesta aceptada
Henry Ing-Simmons
84

Puedes, con un poco de jiggery-pokery no semántico. Mi enfoque habitual es animar la altura de un DIV externo que tiene un solo hijo, que es un DIV sin estilo utilizado solo para medir la altura del contenido.

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>

A uno le gustaría poder prescindir de la .measuringWrappery simplemente configurar la altura del DIV en auto y tener esa animación, pero eso no parece funcionar (la altura se establece, pero no se produce animación).

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>

Mi interpretación es que se necesita una altura explícita para que se ejecute la animación. No puede obtener una animación sobre la altura cuando la altura (la altura inicial o final) es auto.

jhurshman
fuente
10
Dado que esto se basa en javascript, ¡también podría agregar fácilmente el MeasureWrapper usando javascript también!
Quickredfox
18
Puedes hacerlo sin envoltura. Simplemente: función growDiv () {var growDiv = document.getElementById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
usuario1742529
8
Mira esto ... habla de una respuesta simple jsfiddle.net/n5XfG/2596 ... Tu respuesta es increíblemente compleja sin una buena razón. ¡No es necesario max-height, no es necesario auto, y especialmente no es necesario tener Javascript! Solo dos declaraciones simples
VIDesignz
12
@VIDesignz Animes sobre el margen: 100%. ¡Esto es el 100% del ancho del elemento, no su altura! Esto significa que si tiene un elemento con una altura mayor que su ancho, esto no lo ocultará. Además, la velocidad de animación real es totalmente diferente de la que se define.
Timo Türschmann
3
Estaba entusiasmado con la respuesta de VIDesignz hasta que leí más sobre el comentario de Timo. Sí, margin-top(y margin-bottom) son relativos al ancho cuando se definen en porcentajes, sí, eso es estúpido, pero también explica por qué los tiempos de animación de apertura y cierre son completamente diferentes: es lo mismo que se ve en la respuesta mejor calificada, que especifica -código de altura máxima. ¿Por qué es tan difícil hacer lo correcto?
Coderer el
52

Una solución visual para animar la altura usando transiciones CSS3 es animar el relleno en su lugar.

No obtienes el efecto de borrado completo, pero jugar con los valores de duración de transición y relleno debería acercarte lo suficiente. Si no desea establecer explícitamente height / max-height, esto debería ser lo que está buscando.

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/ (diferido de stephband por encima de jsFiddle)

Catarsis
fuente
3
Creo que esta es la mejor respuesta. Esta técnica puede extenderse a ajustar el relleno en los niños también. En mi caso, pude combinarlo con transiciones de altura explícitas en algunos de los contenidos que se están revelando, y el efecto total es mucho más exitoso que la solución de altura máxima, lo cual está bien para la revelación pero introduce un momento incómodo de retraso en ocultar. Prefiero no animar en absoluto que introducir una demora sin sentido que hace que mi aplicación no responda. Me sorprende que tanta gente parezca pensar que es aceptable.
Punto
17
Excepto que aquí no estás animando la altura en absoluto. Estás animando el relleno ... puede desaparecer bien porque puede animarse desde el estado actual hasta 0, pero si observa de cerca cuando se expande, se abre con el texto y luego el relleno solo anima ... porque no No sé cómo animar de 0 a automático ... necesita un rango numérico ... así es como funciona la interpolación.
Rob R
Esta es una buena solución que no es hacky. No es la mejor respuesta a esta pregunta, pero es una alternativa que me funcionó. A veces solo necesitas el efecto de una animación de altura, no la animación completa :)
Ivan Durst
Esta solución también falla cuando se usa un retraso de transición.
Michel Joanisse
También estoy de acuerdo en que esta solución es una solución limpia que proporciona transiciones bastante fluidas si su animación es lo suficientemente rápida (¡en mi caso para un acordeón con transiciones de 0.1s es perfecto!). Sin embargo, no tendrá muchas aplicaciones, ¡pero tampoco los otros responden a esta pregunta!
Remi D
46

Mi solución alternativa es hacer una transición de la altura máxima a la altura exacta del contenido para obtener una animación agradable y fluida, luego usar una devolución de llamada de finalización de transición para establecer la altura máxima en 9999px para que el contenido pueda redimensionarse libremente.

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án
fuente
14
@ Derija93 Eso está usando viejas animaciones de tiempo de espera de JavaScript. El desafío es utilizar transiciones CSS3 en su lugar.
Adam
3
Bueno, sí. Pero, ¿por qué usarías "transiciones CSS3 en su lugar" si, en tu ejemplo, ya estás usando jQuery, una biblioteca, que de todos modos proporciona una gran cantidad de código para "escribir menos, hacer más"? Quería señalar que su versión podría verse mucho mejor, aunque más complicada, si NO estuviera usando jQuery para poder ejecutarse en prácticamente cualquier sitio web. Supongo que lo redacté muy mal. Lo siento por eso. ;)
Kiruse
27
@ Derija93 Quizás porque las transiciones CSS3 tienen (según todas las fuentes que puedo encontrar) un rendimiento mucho mejor que las animaciones jQuery. (Realmente es muy importante para mi caso de uso actual, que me trajo aquí.)
Mark Amery
44
@ Derija93 Porque las animaciones Javascript se ejecutan lentamente en comparación con las transiciones CSS3 bro, y con las animaciones jQuery tienes que preocuparte por el ciclo de animación. Anima algo al hacer clic, luego haz clic rápidamente y observa cómo las animaciones se repiten. Esto se maneja con CSS3.
AlbertEngelB
2
@ Dropped.on.Caprica Esto es un poco viejo ahora. Ya dije que entendí mal el concepto que estaba tratando de demostrar. De todos modos, para animaciones simples, .stophace el truco para el problema de bucle de animación bastante complejo. Ahora, cuando animas la altura y el color pero deseas interrumpir solo la animación de altura, las cosas se vuelven un poco más complicadas ... Entiendo tu punto.
Kiruse
43

La respuesta aceptada funciona para la mayoría de los casos, pero no funciona bien cuando divpuede variar mucho en altura: la velocidad de la animación no depende de la altura real del contenido y puede verse entrecortada.

Todavía puede realizar la animación real con CSS, pero necesita usar JavaScript para calcular la altura de los elementos, en lugar de intentar usarlo auto. No se requiere jQuery, aunque es posible que deba modificar esto un poco si desea compatibilidad (funciona en la última versión de Chrome :)).

window.toggleExpand = function(element) {
    if (!element.style.height || element.style.height == '0px') { 
        element.style.height = Array.prototype.reduce.call(element.childNodes, function(p, c) {return p + (c.offsetHeight || 0);}, 0) + 'px';
    } else {
        element.style.height = '0px';
    }
}
#menu #list {
    height: 0px;
    transition: height 0.3s ease;
    background: #d5d5d5;
    overflow: hidden;
}
<div id="menu">
    <input value="Toggle list" type="button" onclick="toggleExpand(document.getElementById('list'));">
    <ul id="list">
        <!-- Works well with dynamically-sized content. -->
        <li>item</li>
        <li><div style="height: 100px; width: 100px; background: red;"></div></li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>

Oleg Vaskevich
fuente
Esto puede ser útil, pero no puedo decirlo porque no sé cómo usarlo sin un mejor ejemplo.
Ivan Durst
1
Simplemente pasa cualquier elemento DOM (por ejemplo, desde document.getElementById('mydiv')o $('#mydiv').get()), y la función alterna entre ocultarlo / mostrarlo. Si configura las transiciones CSS, el elemento se animará automáticamente.
Oleg Vaskevich
Agregué un fragmento. Esto tiene la ventaja de funcionar bien para contenido de tamaño dinámico.
Oleg Vaskevich
3
-1, ya que esto no es "usar CSS". El punto de la pregunta es para una solución libre de JS. Creo que la respuesta "correcta", eso no es un truco, sería esta, sin embargo ...
dudewad
16
Voto negativo por usar JS, ¿en serio? La mitad de las respuestas aquí usan JS, por lo que su voto negativo no parece justo o necesario. Además, esta respuesta todavía usa CSS para realizar la animación real, que IMO era el punto del OP. Para lo único que se usa JS es para calcular la altura real, ya que es imposible hacerlo con CSS puro (y la configuración min-heightcomo en la respuesta esperada causa problemas con la velocidad de la animación cuando el tamaño de la lista puede variar mucho).
Oleg Vaskevich
30

Hubo poca mención de la Element.scrollHeightpropiedad que puede ser útil aquí y aún se puede usar con una transición CSS pura. La propiedad siempre contiene la altura "completa" de un elemento, independientemente de si su contenido se desborda y cómo se desborda como resultado de la altura contraída (por ejemplo height: 0).

Como tal, para un elemento height: 0(efectivamente colapsado por completo), su altura "normal" o "completa" todavía está fácilmente disponible a través de su scrollHeightvalor (invariablemente, una longitud de píxel ).

Para tal elemento, suponiendo que ya tiene la transición configurada como, por ejemplo, (usando ulsegún la pregunta original):

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

Podemos desencadenar la "expansión" animada deseada de altura, usando solo CSS, con algo como lo siguiente (aquí asumiendo que la ulvariable se refiere a la lista):

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

Eso es. Si necesita contraer la lista, cualquiera de las dos siguientes afirmaciones funcionará:

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

Mi caso de uso particular, giraba en torno a la animación de las listas de desconocido y longitudes menudo considerables, así que no estaba cómoda de decidirse por una arbitraria "suficientemente grande" heighto max-heightespecificación y correr el riesgo de corte del contenido o contenido que de repente se necesita desplazamiento (si overflow: auto, por ejemplo) . Además, la flexibilización y el tiempo se rompen con max-heightsoluciones basadas, porque la altura utilizada puede alcanzar su valor máximo mucho antes de lo que tomaría max-heightalcanzar 9999px. Y a medida que crecen las resoluciones de pantalla, las longitudes de píxeles como 9999pxdejan un mal sabor de boca. Esta solución particular resuelve el problema de una manera elegante, en mi opinión.

Finalmente, esperamos que las futuras revisiones de CSS aborden la necesidad de los autores de hacer este tipo de cosas de manera aún más elegante: revisar la noción de valores "calculados" frente a "usados" y "resueltos", y considerar si las transiciones deberían aplicarse a las calculadas valores, incluidas las transiciones con widthy height(que actualmente reciben un poco de un tratamiento especial).

amn
fuente
66
No lo encontró en las otras respuestas aquí porque interactuar con el DOM usando la Element.scrollHeightpropiedad requiere JavaScript y, como dice claramente la pregunta, quieren hacerlo sin JavaScript.
TylerH
3
Hay muchas otras partes de CSS además de esas dos pseudo-clases que pueden resolver este problema, como lo muestran las respuestas más votadas en la página 1. Establecer un estilo en JavaScript no es "CSS puro", ya que requiere que JS se configure en primer lugar. A menudo, cuando una persona solicita una solución solo de CSS, es porque no puede inyectar ningún JavaScript, o su proyecto o rol actual no se lo permite.
TylerH
29

Úselo max-heightcon diferente transición de alivio y retraso 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;
}

Ver ejemplo: http://jsfiddle.net/0hnjehjc/1/

malihu
fuente
12
El problema aquí es que, para asegurarse de tener suficiente espacio en un entorno dinámico, debe usar alturas máximas ridículas como 999999px. Esto, por otro lado, cambiará su animación, porque los cuadros se calculan a partir de este valor. Por lo tanto, puede terminar con un "retraso" de animación en una dirección y un final realmente rápido en la otra dirección (corr: funciones de tiempo)
Julian F. Weinert
29

No hay valores codificados.

Sin JavaScript

Sin aproximaciones

El truco consiste en utilizar un oculto y duplicado divpara que el navegador entienda lo que significa 100%.

Este método es adecuado siempre que pueda duplicar el DOM del elemento que desea 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
fuente
Bien hecho: esta es una solución válida para un problema que realmente no debería existir. Animar max-height es bas a menos que la transición se establezca en "lineal", también es (obviamente) muy malo para animar contenido CMS donde la altura es variable.
M_Willett
esto es inteligente, pero no me sentiría cómodo duplicando elementos DOM y contenido para esto.
Andre Figueiredo
Puede hacerlo sin un contenedor y más rendimiento animando transform: scaleY(1), ya que esta transición no elimina el espacio.
Fabian von Ellerts
21

Mientras publico esto, ya hay más de 30 respuestas, pero siento que mi respuesta mejora en la respuesta ya aceptada por Jake.

No estaba contento con el problema que surge del simple uso max-heighty las transiciones CSS3, ya que, como muchos comentadores notaron, debe establecer su max-heightvalor muy cerca de la altura real o obtendrá un retraso. Vea este JSFiddle para ver un ejemplo de ese problema.

Para evitar esto (sin usar JavaScript), agregué otro elemento HTML que transiciona el transform: translateYvalor CSS.

Esto significa ambos max-heighty translateYse usan: max-heightpermite que el elemento empuje hacia abajo los elementos debajo de él, mientras translateYda el efecto "instantáneo" que queremos. El problema con max-heighttodavía existe, pero su efecto se reduce. Esto significa que puede establecer una altura mucho mayor para su max-heightvalor y preocuparse menos por ello.

El beneficio general es que en la transición de regreso (el colapso), el usuario ve la translateYanimación de inmediato, por lo que realmente no importa cuánto tiempo max-heighttome.

Solución como violín

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
fuente
Bonita comparación @davidtaubmann, ¡tu pluma ilustra muy bien las diferencias! Fui con translateYmás scaleporque no me gustó cómo scaledio ese efecto de compresión.
Andrew Messier
Gracias @ andrew-messier, eliminé el comentario porque hay un error, translateY no está excediendo en codepen.io/davidtaubmann/pen/zKZvGP (porque no hay un niño para hacer el movimiento, necesita una solución) ... pero en esto En uno solo podemos ver la comparación desde SOLO MAX-HEIGHT y mezclarlo con la escala (como se indica en otras respuestas): codepen.io/davidtaubmann/pen/jrdPER . Todo esto me ayudó mucho con la metodología Target que estoy usando
DavidTaubmann
18

Ok, entonces creo que se me ocurrió una respuesta súper simple ... no max-height, usa relativeposicionamiento, trabaja en lielementos y es puro CSS. No he probado nada más que Firefox, aunque a juzgar por el CSS, debería funcionar en todos los 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
fuente
13
Esto funcionará hasta que la altura del elemento exceda su ancho. Es la base de cómo se calculan los márgenes usando porcentajes: se calculan en función del ancho del elemento. Entonces, si tiene un elemento de 1000 px de ancho, entonces un elemento de 1100 px será demasiado grande para que esta solución funcione, lo que significa que tendría que aumentar ese margen superior negativo. Básicamente, es exactamente el mismo problema que usar height o max-height.
dudewad
15

EDITAR: desplácese hacia abajo para obtener una respuesta actualizada.
Estaba haciendo una lista desplegable y vi esta publicación ... muchas respuestas diferentes, pero decido compartir mi lista desplegable también ... No es perfecto, pero al menos usará solo CSS para ¡desplegable! He estado usando transform: translateY (y) para transformar la lista a la vista ...
Puede ver más en la prueba
http://jsfiddle.net/BVEpc/4/
He colocado div detrás de cada li porque mi la lista desplegable proviene de arriba y para mostrarles correctamente esto era necesario, mi código div es:

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

y pasar el mouse es:

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

y debido a que la altura ul está establecida en el contenido, puede superar el contenido de su cuerpo, por eso hice esto para ul:

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

y flotar:

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

la segunda vez después de la transición es demorada y se ocultará después de que mi lista desplegable se haya cerrado animadamente ...
Espero que alguien más se beneficie de esta.

EDITAR: ¡No puedo creer que las personas realmente usen este prototipo! ¡Este menú desplegable es solo para un submenú y eso es todo! He actualizado uno mejor que puede tener dos submenús para la dirección ltr y rtl con soporte para IE 8.
Fiddle para LTR
Fiddle para RTL
espero que alguien encuentre esto útil en el futuro.

Sijav
fuente
11

Puede pasar de la altura: 0 a la altura: automático siempre que también proporcione la altura mínima y la 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
fuente
@Stuart Badminton, ¿puede proporcionar un ejemplo de trabajo? He reunido esto, pero no parece funcionar en ningún lado :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin
55
El problema es con su regla de transición. para que funcione, también debe aplicar la transición a la altura mínima y máxima. transición: altura .5s lineal, altura mínima .5s lineal, altura máxima .5s lineal
Stuart Badminton
2
Esto es lo que tengo más adelante, tal como dijiste, que necesitaba para animar la altura máxima: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin
11
Por supuesto, hay un problema con esto. El tiempo que lleva animar la altura máxima no es el tiempo que lleva la transición a la altura automática de la caja: llega a la altura: auto primero, antes de que max-height haya terminado de animarse. De manera similar, cuando se pasa a 0, la transición no comienza de inmediato, ya que la altura máxima comienza a una altura mayor que la altura: auto. Transición a la altura máxima: 1000 px deja esto claro: jsfiddle.net/n5XfG/11
stephband
Tienes razón, en más pruebas me di cuenta de esto. No sé si alguna vez se implementará una altura completa: 0 a altura: automática, pero el uso de una altura máxima en algunas situaciones resuelve una situación que de otro modo sería imposible usando solo CSS.
Stuart Badminton
10

Solución Flexbox

Pros:

  • simple
  • no JS
  • transición suave

Contras:

  • El elemento debe colocarse en un contenedor flexible de altura fija

La forma en que funciona es tener siempre una base flexible: auto en el elemento con contenido, y en cambio en cambio flex-grow y flex-shrink.

Editar: JS Fiddle mejorado inspirado en la interfaz 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
fuente
7

Ampliando la respuesta de @jake, la transición irá hasta el valor de altura máxima, causando una animación extremadamente rápida: si configura las transiciones para ambos: desplazarse y apagarse, puede controlar la velocidad loca un poco más.

Por lo tanto, li: hover es cuando el mouse ingresa al estado y luego la transición en la propiedad no suspendida será la salida del mouse.

Esperemos que esto sea de alguna ayuda.

p.ej:

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

Aquí hay un violín: http://jsfiddle.net/BukwJ/

jamie
fuente
1
No sé por qué esto obtuvo un -1. De hecho, creo que este es un mejor intento que algunas de las personas que agregan un montón de javascript. No es una solución perfecta, pero con algunos ajustes hace un buen trabajo, encontré que esto funciona por el valor de retorno. transición: todos los 500 ms de cubic-bezier (0.000, 1.225, 0.085, 0.385); Esto se basó en una transición de 2 segundos en la apertura y luego en el código anterior para volver al estado inicial. Gracias ... Esto me ayudó mejor que todo lo anterior. +1 Usé
gcoulby
OK, tomó un poco más de ajustes. transición: todos los 500 ms de cubic-bezier (0.000, 1.390, 0.000, 0.635); Debo especificar que mi transición va del 5% al ​​100% (pero en realidad el valor final es del 28%), por lo que depende del proyecto.
gcoulby
Absolutamente, no es lo ideal, pero es una solución rápida si conoce la altura promedio.
jamie
7

Creo que se me ocurrió una solución realmente sólida.

¡OKAY! Sé que este problema es tan antiguo como Internet, pero creo que tengo una solución que convertí en un complemento llamado transición mutante . Mi solución establece los style=""atributos para los elementos rastreados siempre que haya un cambio en el DOM. el resultado final es que puede usar un buen CSS para sus transiciones y no usar soluciones hacky o javascript especial. Lo único que tiene que hacer es configurar lo que desea rastrear en el elemento en cuestión utilizando data-mutant-attributes="X".

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

¡Eso es! Esta solución utiliza MutationObserver para seguir los cambios en el DOM. Debido a esto, realmente no tiene que configurar nada ni usar javascript para animar cosas manualmente. Los cambios se rastrean automáticamente. Sin embargo, debido a que usa MutationObserver, esto solo hará la transición en IE11 +.

Violines!

JonTroncoso
fuente
12
Por lo general, cuando alguien pregunta cómo hacer algo "sin JavaScript", significa que la solución debe funcionar en los navegadores que tienen JavaScript deshabilitado. No significa "sin escribir mis propios guiones". Por lo tanto, decir que su complemento se puede usar sin usar JavaScript es, en el mejor de los casos, engañoso, considerando que es un complemento de JavaScript.
BoltClock
Debo aclarar, cuando digo que no tiene que usar ningún javascript especial, quiero decir que no tiene que escribir ningún javascript. solo incluya la biblioteca JS y especifique qué atributos desea ver en el HTML. No tiene que usar css de altura fija, ni descifrar nada. solo estilo y listo.
JonTroncoso
"(...) realmente no tienes que configurar nada o usar javascript para animar las cosas manualmente (...)" así que estás usando JavaScript por diversión
Roko C. Buljan
6

Aquí hay una manera de hacer la transición desde cualquier altura inicial, incluido 0, a automático (tamaño completo y flexible) sin requerir un código fijo por nodo o cualquier código de usuario para inicializar: https://github.com/csuwildcat / transición-auto . Esto es básicamente el santo grial para lo que quieres, creo -> http://codepen.io/csuwldcat/pen/kwsdF . Simplemente coloque el siguiente archivo JS en su página, y todo lo que necesita hacer después de eso es agregar / eliminar un solo atributo booleano - reveal=""- de los nodos que desea expandir y contraer.

Esto es todo lo que necesita hacer como usuario, una vez que incluya el bloque de código que se encuentra debajo del código de ejemplo:

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

Aquí está el bloque de código para incluir en su página, después de eso, todo es salsa:

Suelte este archivo JS en su página; todo funciona Just Works ™

/ * Código de altura: auto; en transición * /

(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
fuente
99
Si bien esta es una solución mucho más limpia y flexible que las otras que se han sugerido, personalmente creo que esto no debería ser algo que JS debería tocar en absoluto. No digo que estés equivocado, es solo un suspiro.
mystrdat
@mystrdat para ser claro, sin embargo, JS solo se usa para observar ciertas condiciones y agregar un par de atributos / valores CSS temporales en función de los eventos / condiciones que está buscando. La transición todavía se realiza en CSS. Aunque estoy de acuerdo, ¡es triste que este nivel de configuración y agarre manual sea necesario para satisfacer verdaderamente un caso de uso aparentemente simple! : /
csuwldcat
1
@KarolisRamanauskas no, eso no es un problema: IE es compatible con CSS Keyframes y requestAnimationFrame. Estaba usando clipmi activador de propiedad ficticia, y por alguna razón IE dejó de permitir que el clip se animara. Cambié a la opacidad, y todo funciona de nuevo: codepen.io/csuwldcat/pen/FpzGa . He actualizado el código en la respuesta para que coincida.
csuwldcat
1
@csuwldcat Muy buena solución, noté que el contenido aparece y desaparece por una pequeña fracción, ¿por qué sucede esto? ¿Es evitable?
Beto Aveiga
12
Quiero votar esto, pero en lugar de responder a la pregunta de cómo hacerlo funcionar, compartiste un complemento que escribiste para hacerlo funcionar. Nosotros, los curiosos, tenemos que aplicar ingeniería inversa a su complemento, lo que no es muy divertido. Deseo que actualice su respuesta para contener más explicaciones de lo que hace su complemento y por qué. Cambia el código para ser más explicativo. Por ejemplo, tiene una sección completa de código que solo escribe CSS estático. Prefiero ver el CSS, que el código que lo genera. Puede omitir las partes aburridas, como repetir para todos los prefijos del navegador.
gilly3
6

La respuesta de Jake para animar la altura máxima es genial, pero encontré molesto el retraso causado por establecer una altura máxima.

Se podría mover el contenido plegable a una división interna y calcular la altura máxima obteniendo la altura de la división interna (a través de JQuery sería la altura externa ()).

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

Aquí hay un enlace jsfiddle: http://jsfiddle.net/pbatey/duZpT

Aquí hay un jsfiddle con la cantidad mínima absoluta de código requerida: http://jsfiddle.net/8ncjjxh8/

pbatey
fuente
5

Me doy cuenta de que este hilo está envejeciendo, pero ocupa un lugar destacado en ciertas búsquedas de Google, así que creo que vale la pena actualizarlo.

También solo obtiene / establece la altura del elemento:

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

Debería volcar este Javascript inmediatamente después de la etiqueta de cierre de target_box en una etiqueta de script en línea.

Charley
fuente
document.getElementById ('target_box'). getBoundingClientRect (). height debería funcionar mejor.
alterar
5

Pude hacer esto. Tengo un .childy un .parentdiv. El div hijo se adapta perfectamente al ancho / alto de los padres con el absoluteposicionamiento. Luego animé la translatepropiedad para Yreducir su valor 100%. Es una animación muy suave, sin fallas ni inconvenientes como cualquier otra solución aquí.

Algo como esto, pseudocó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 */
}

Se podría especificar un heightsobre .parent, en px, %o como licencia auto. Este div luego enmascara el .childdiv cuando se desliza hacia abajo.

Josh Ribakoff
fuente
El ejemplo de trabajo de esto será muy apreciado.
Neurotransmisor
5

Publiqué una respuesta con JavaScript y obtuve un voto negativo, así que me molesté e intenté nuevamente, ¡y lo descifré solo con CSS!

Esta solución utiliza algunas 'técnicas':

  • padding-bottom:100%'piratear' donde los porcentajes se definen en términos del ancho actual del elemento. Más información sobre esta técnica.
  • envoltura retráctil de flotador (que necesita un div adicional para aplicar el corte de limpieza de flotador)
  • uso no semántico de https://caniuse.com/#feat=css-writing-mode y algunas transformaciones para deshacerlo (esto permite el uso del relleno de relleno anterior en un contexto vertical)

Sin embargo, el resultado es que obtenemos una transición eficiente usando solo CSS y una única función de transición para lograr la transición sin problemas; ¡el Santo Grial!

Por supuesto, hay un inconveniente! No puedo averiguar cómo controlar el ancho con el que se corta el contenido ( overflow:hidden); debido al hack de fondo acolchado, el ancho y la altura están íntimamente relacionados. Sin embargo, puede haber una manera, así que volveremos a ella.

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
fuente
La otra desventaja de esto es que no puede mover el cursor fuera del div "hoverme" a la lista de elementos para interactuar con ellos (aparentemente la razón para preguntar OP), por lo que solo es útil como una función de información sobre herramientas en lugar de , digamos, un menú con submenús anidados.
TylerH
Eso es puramente una consecuencia de la forma en que se formuló la pregunta y no está relacionada con la técnica. Puede cambiar el activador para que sea un en input[type="checkbox"]:checkedlugar de :hoversi lo desea (es decir, no desea usar JavaScript para agregar ninguna clase)
EoghanM
Quiero decir, independientemente de cómo se formule la pregunta, si la implementación de una solución es tan limitada, probablemente debería mencionarse en la respuesta de esa solución (o preferiblemente explicarse ). Mencionas que esta solución es un 'santo grial', pero el div en transición ni siquiera se puede pasar por alto ... no me parece una solución tan definitiva.
TylerH
Hola @TylerH, disculpas, me perdí el hecho de que el disparador en la pregunta original rodea la lista ampliada, por lo que la lista debe permanecer abierta cuando se pasa el cursor. He actualizado mi respuesta para reflejar esto. Sin embargo, como mencioné, la forma en que se desencadena no es tan pertinente, ya que estoy presentando una nueva técnica que anteriormente no se creía posible solo en CSS (he estado buscando una solución durante algunos años).
EoghanM
4

Aquí hay una solución que acabo de usar en combinación con jQuery. Esto funciona para la siguiente estructura 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>

y la función:

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

Luego puede usar una función de clic para establecer y eliminar la 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
fuente
esto no se muestra correctamente con la última versión de jquery
oliverbachmann
4

Recientemente he estado haciendo la transición max-heightde los lielementos en lugar del ajuste ul.

El razonamiento es que la demora para pequeño max-heightses mucho menos notable (si es que lo hace) en comparación con grande max-heights, y también puedo establecer mi max-heightvalor en relación con el font-sizede en lilugar de un gran número arbitrario usando emso rems.

Si el tamaño de mi fuente es 1rem, lo configuraré max-heighten algo como 3rem(para acomodar texto envuelto). Puedes ver un ejemplo aquí:

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

silencio mental
fuente
3

No he leído todo en detalle, pero recientemente tuve este problema e hice lo siguiente:

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

Esto le permite tener un div que al principio muestra contenido de hasta 200 px de altura y, al pasar el mouse, su tamaño se vuelve al menos tan alto como todo el contenido del div. El Div no se convierte en 3000 px, pero 3000 px es el límite que estoy imponiendo. Asegúrese de tener la transición en el non: hover, de lo contrario, podría obtener una representación extraña. De esta manera, el: hover hereda del no: hover.

La transición no funciona de px a% o a auto. Necesita usar la misma unidad de medida. Esto funciona bien para mi. Usar HTML5 lo hace perfecto ...

Recuerde que siempre hay una solución alternativa ...; )

Espero que alguien encuentre esto útil

work.stella84
fuente
3

La solución de altura máxima de Jake funciona bien, si el valor de altura máxima codificado proporcionado no es mucho mayor que la altura real (porque de lo contrario hay retrasos y problemas de sincronización indeseables). Por otro lado, si el valor codificado de forma accidental no es mayor que la altura real, el elemento no se abrirá por completo.

La siguiente solución de CSS también requiere un tamaño codificado que debería ser más grande que la mayoría de los tamaños reales existentes. Sin embargo, esta solución también funciona si el tamaño real es en algunas situaciones mayor que el tamaño codificado. En ese caso, la transición puede saltar un poco, pero nunca dejará un elemento parcialmente visible. Por lo tanto, esta solución también podría usarse para contenido desconocido, por ejemplo, desde una base de datos, donde solo sabe que el contenido generalmente no es mayor que x píxeles, pero hay excepciones.

La idea es utilizar un valor negativo para margen inferior (o margen superior para una animación ligeramente diferente) y colocar el elemento de contenido en un elemento central con desbordamiento: oculto. El margen negativo del elemento de contenido reduce la altura del elemento del medio.

El siguiente código utiliza una transición en el margen inferior de -150 px a 0 px. Esto solo funciona bien siempre que el elemento de contenido no sea superior a 150 px. Además, utiliza una transición en altura máxima para el elemento medio de 0px a 100%. Esto finalmente oculta el elemento del medio si el elemento de contenido es superior a 150 px. Para la altura máxima, la transición solo se usa para retrasar su aplicación por un segundo al cerrar, no para un efecto visual uniforme (y, por lo tanto, puede correr 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>

El valor para el margen inferior debe ser negativo y lo más cercano posible a la altura real del elemento de contenido. Si (el valor absoluto) es mayor, existen problemas similares de retraso y sincronización que con las soluciones de altura máxima, que sin embargo pueden limitarse siempre que el tamaño codificado no sea mucho mayor que el real. Si el valor absoluto para margen inferior es menor que la altura real, la tansición salta un poco. En cualquier caso, después de la transición, el elemento de contenido se muestra completamente o se elimina por completo.

Para obtener más detalles, consulte la publicación de mi blog http://www.taccgl.org/blog/css_transition_display.html#combined_height

Helmut Emmelmann
fuente
3

Puede hacer esto creando una animación inversa (colapso) con clip-path.

#child0 {
    display: none;
}
#parent0:hover #child0 {
    display: block;
    animation: height-animation;
    animation-duration: 200ms;
    animation-timing-function: linear;
    animation-fill-mode: backwards;
    animation-iteration-count: 1;
    animation-delay: 200ms;
}
@keyframes height-animation {
    0% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 0%, 0% 0%);
    }
    100% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 100%, 0% 100%);
    }
}
<div id="parent0">
    <h1>Hover me (height: 0)</h1>
    <div id="child0">Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>
    </div>
</div>

Andrii
fuente
2

Esto no es exactamente una "solución" al problema, sino más bien una solución alternativa. Solo funciona como está escrito con texto, pero puedo cambiarlo para que funcione con otros elementos según sea necesario, estoy seguro.

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

Aquí hay un ejemplo: http://codepen.io/overthemike/pen/wzjRKa

Esencialmente, establece el tamaño de fuente en 0 y realiza la transición en lugar de la altura, o la altura máxima, o scaleY (), etc. a un ritmo lo suficientemente rápido como para que la altura se transforme a lo que desea. Actualmente, no es posible transformar la altura real con CSS en automático, pero transformar el contenido dentro es, por lo tanto, la transición del tamaño de fuente.

  • Nota: hay javascript en el codepen, pero su único propósito es agregar / eliminar clases css al hacer clic para el acordeón. Esto se puede hacer con botones de radio ocultos, pero no estaba enfocado en eso, solo en la transformación de la altura.
Mike S.
fuente
1
Duplica efectivamente esta respuesta , pero omite el efecto de opacidad de desvanecimiento.
SeldomNeedy
Convenido. Mejor enfoque
Mike S.