Transiciones en la propiedad de visualización CSS

1449

Actualmente estoy diseñando un menú 'mega desplegable' de CSS, básicamente un menú desplegable regular solo de CSS, pero que contiene diferentes tipos de contenido.

Por el momento, parece que las transiciones CSS 3 no se aplican a la propiedad 'display' , es decir, no se puede hacer ningún tipo de transición de display: nonea display: block(o cualquier combinación).

¿Hay alguna forma de que el menú de segundo nivel del ejemplo anterior se desvanezca cuando alguien pasa el cursor sobre uno de los elementos del menú de nivel superior?

Soy consciente de que puede usar transiciones en la visibility:propiedad, pero no puedo pensar en una forma de usar eso de manera efectiva.

También intenté usar la altura, pero eso simplemente falló miserablemente.

También soy consciente de que es trivial lograr esto usando JavaScript, pero quería desafiarme a mí mismo para usar solo CSS, y creo que me estoy quedando un poco corto.

RichardTape
fuente
¿Funciona la transición CSS si la aplica a la propiedad de opacidad, en lugar de mostrarla?
Paul D. Waite
13
posición: absoluta; visibilidad: oculta; es lo mismo que mostrar: ninguno;
Jawad
8
@Jawad: Solo si agrega algo z-index:0como también.
DanMan
77
@Jawad: se recomienda nunca usarlo a visibility: hiddenmenos que desee que los lectores de pantalla lo lean (mientras que los navegadores típicos no lo harán). Solo define la visibilidad del elemento (como decir opacity: 0), y aún se puede seleccionar, hacer clic y lo que solía ser; Simplemente no es visible.
Forest Katsch
1
no es compatible con pointer-eventsIE 8,9,10, por lo que no siempre está bien
Steven Pribilinskiy

Respuestas:

1352

Puede concatenar dos transiciones o más, y visibilityesta vez es lo que resulta útil.

div {
  border: 1px solid #eee;
}
div > ul {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s linear;
}
div:hover > ul {
  visibility: visible;
  opacity: 1;
}
<div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
</div>

(No olvide los prefijos de proveedor de la transitionpropiedad).

Más detalles están en este artículo .

Guillermo
fuente
10
Esta respuesta parece menos trabajo que las otras y logra lo que esperaríamos de la pantalla: none / block; Gracias. Me ahorró un montón de tiempo.
Brendan
21
Sí, el problema con esto es que algo detrás se superpondrá incluso si no es visible. El uso de height: 0 es una solución mucho mejor
Josh Bedo
742
Esto es bueno, pero el problema es que los elementos de "visibilidad oculta" todavía ocupan espacio mientras que "no mostrar ninguno" no lo hace.
Rui Marques
41
Probablemente me falta algo, pero ¿por qué altera tanto la visibilidad como la opacidad? No establecer la opacidad en 0 oculta el elemento. ¿Por qué necesita establecer también la visibilidad como oculta?
GMA
21
@GeorgeMillo si establece solo la opacidad, el elemento todavía está en la representación de la página (por ejemplo, no puede hacer clic en pensamiento).
adriendenat
801

Debe ocultar el elemento por otros medios para que esto funcione.

Logré el efecto colocando ambos <div>s absolutamente y configurando el oculto en opacity: 0.

Si incluso alterna la displaypropiedad de nonea block, su transición en otros elementos no ocurrirá.

Para evitar esto, siempre permita que el elemento sea display: block, pero oculte el elemento ajustando cualquiera de estos medios:

  1. Establecer el heighta 0.
  2. Establecer el opacitya 0.
  3. Coloque el elemento fuera del marco de otro elemento que tiene overflow: hidden.

Es probable que haya más soluciones, pero no puede realizar una transición si cambia el elemento a display: none. Por ejemplo, puede intentar probar algo como esto:

div {
    display: none;
    transition: opacity 1s ease-out;
    opacity: 0;
}
div.active {
    opacity: 1;
    display: block;
}

Pero eso no funcionará. Desde mi experiencia, he encontrado que esto no hace nada.

Debido a esto, siempre necesitarás mantener el elemento display: block, pero puedes evitarlo haciendo algo como esto:

div {
    transition: opacity 1s ease-out;
    opacity: 0;
    height: 0;
    overflow: hidden;
}
div.active {
    opacity: 1;
    height: auto;
}
Jim Jeffers
fuente
29
Gracias Jim por una respuesta completa. Tiene toda la razón sobre el hecho de que si la visualización: la propiedad cambia en absoluto, TODAS sus transiciones no funcionarán. Lo cual es una pena, me pregunto cuál es el razonamiento detrás de eso. En una nota al margen, en el mismo enlace que publiqué en la pregunta original, puedes ver dónde estoy con ella. El único (pequeño) problema que tengo es en Chrome [5.0.375.125] cuando se carga la página, puede ver que el menú se desvanece rápidamente a medida que los elementos se cargan en la página. Firefox 4.0b2 y Safari 5.0 están absolutamente bien ... ¿error o algo que me he perdido?
RichardTape
77
Estoy de acuerdo en que esto es correcto y contribuiré con esto; un aviso para futuros viajeros. Encontré una solución que funciona en Chrome, pero descubrí que falla en el iPhone 4: esto visibility:hidden; opacity:0; -webkit-transition: all .2s ease-in-out;no solo no hace la transición correctamente, sino que el elemento de destino nunca aparecerá. QA te fallará a ti, a mí y a tu madre.
SimplGy 05 de
1
En realidad, también puede hacer la transición de la propiedad de visibilidad ... solo diciendo
Cu7l4ss
2
Si configura su estado oculto height: 0;y no lo hace, la transición no funcionará. Intenté esto solo tratando de hacer la transición de la opacidad. Tuve que quitar elheight: 0;
chovy
10
¡Acabas de ganar un voto por las overflow: hiddengracias!
thiagoh
279

En el momento de esta publicación, todos los principales navegadores deshabilitan las transiciones CSS si intenta cambiar la displaypropiedad, pero las animaciones CSS aún funcionan bien para que podamos usarlas como una solución alternativa.

Código de ejemplo (puede aplicarlo a su menú en consecuencia) Demostración :

Agregue el siguiente CSS a su hoja de estilo:

@-webkit-keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}
@keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}

Luego aplique la fadeInanimación al niño en el desplazamiento del padre (y, por supuesto, establecido display: block):

.parent:hover .child {
    display: block;
    -webkit-animation: fadeIn 1s;
    animation: fadeIn 1s;
}

Actualización 2019: método que también admite la desaparición gradual:

(Se requiere algún código JavaScript)

// We need to keep track of faded in elements so we can apply fade out later in CSS
document.addEventListener('animationstart', function (e) {
  if (e.animationName === 'fade-in') {
      e.target.classList.add('did-fade-in');
  }
});

document.addEventListener('animationend', function (e) {
  if (e.animationName === 'fade-out') {
      e.target.classList.remove('did-fade-in');
   }
});
div {
    border: 5px solid;
    padding: 10px;
}

div:hover {
    border-color: red;
}

.parent .child {
  display: none;
}

.parent:hover .child {
  display: block;
  animation: fade-in 1s;
}

.parent:not(:hover) .child.did-fade-in {
  display: block;
  animation: fade-out 1s;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
<div class="parent">
    Parent
    <div class="child">
        Child
    </div>
</div>

Salman von Abbas
fuente
3
Gracias por esto. El height: 0truco (para las transiciones) mencionado anteriormente no parece funcionar porque la altura se establece en 0 en la transición de desvanecimiento, pero este truco parece funcionar bien.
Elliot Winkler
41
Gracias muy útil ¿Pero cómo desvanecerlo?
Illiou
1
El primer párrafo de esta respuesta no tiene mucho sentido. Los navegadores no solo deshabilitan todas las transiciones directamente en el momento en que usa la displaypropiedad, realmente no hay razón para hacerlo. E incluso si lo hicieran , ¿por qué funcionarían las animaciones entonces? Tampoco puedes usar la displaypropiedad en animaciones CSS.
BoltClock
77
La mejor respuesta, no es posible con transiciones pero sí con animaciones.
Mikel
66
¿Qué tal la animación inversa, que es cuando el elemento debe desaparecer lentamente?
Verde
77

Sospecho que la razón por la que las transiciones se desactivan si displayse cambia es por lo que realmente hace la pantalla. No , no cambia nada que posiblemente podría ser animado sin problemas.

display: none;y visibility: hidden;son dos cosas completamente diferentes.
Ambos tienen el efecto de hacer que el elemento sea invisible, pero visibility: hidden;aún se representa en el diseño, pero no de manera visible .
El elemento oculto todavía ocupa espacio, y todavía se representa en línea o como un bloque o bloque en línea o tabla o lo que sea que el displayelemento le indique que represente, y ocupa espacio en consecuencia.
Otros elementos no se mueven automáticamente para ocupar ese espacio. El elemento oculto simplemente no representa sus píxeles reales en la salida.

display: nonepor otro lado, en realidad evita que el elemento se procese por completo .
No ocupa ningún espacio de diseño.
Otros elementos que habrían ocupado parte o la totalidad del espacio ocupado por este elemento ahora se ajustan para ocupar ese espacio, como si el elemento simplemente no existiera en absoluto .

displayNo es solo otro atributo visual.
Establece todo el modo de representación del elemento, como por ejemplo si se trata de un block, inline, inline-block, table, table-row, table-cell, list-item, o lo que sea!
Cada uno de los que tienen muy diferentes ramificaciones de diseño y no habría ninguna forma razonable de animado o sin problemas la transición de ellos (trate de imaginar una transición suave de blocka inlineo viceversa, por ejemplo!).

Esta es la razón por la cual las transiciones se deshabilitan si la visualización cambia (incluso si el cambio es hacia o desde none, noneno es simplemente invisibilidad, ¡es su propio modo de representación de elementos que significa que no hay representación en absoluto!).

Joel_MMCC
fuente
8
Por muy buenas que sean las soluciones anteriores, fue muy satisfactorio obtener una explicación sensata de por qué las transiciones no se aplican a los atributos de visualización.
kqr
8
Estoy en desacuerdo. Podría tener sentido completo. Si display: none to display: el bloqueo se produjo instantáneamente al comienzo de la transición, sería genial. Y para la transición de regreso, si pasara de la pantalla: bloque a pantalla: ninguno justo al final de la transición, sería perfecto.
Curtis Yallop
2
Si no puede imaginar cómo se ve una animación desde un punto hasta un rectángulo, tiene problemas. Una transición de no ocupar espacio a ocupar un espacio rectangular es MUY obvio, como, cada vez que arrastra un rectángulo con el mouse como en todas las aplicaciones. Como resultado de esta falla, hay tantos hacks que involucran la altura máxima y los márgenes negativos, es ridículo. Lo único que funciona es almacenar en caché la altura 'real' y luego animar de cero al valor almacenado en caché con JavaScript. Triste.
Triynko
2
Tryniko: display: none no cambia el tamaño del elemento a un rectángulo de 0 x 0, lo elimina del DOM. Puede animar de un rectángulo a un punto animando las propiedades de ancho y alto a cero y luego los otros elementos fluirán a su alrededor como si tuviera 'display: none' pero su atributo 'display' permanecería 'block'; el elemento todavía está en el DOM. El concepto de animación entre display: block y display: none es ridículo: no hay estados intermedios. Un elemento existe en el DOM o no, no importa cuán pequeño o invisible sea.
Steve Thorpe
1
No es ridículo animar propiedades discretas. Las propiedades discretas como displayno se pueden hacer una transición suave como otras propiedades, pero eso no significa que el tiempo no sea importante. Poder controlar cuándo ocurre la transición de disply:blocka display:nonees muy importante. Tal como lo describió @CurtisYallop. Si quiero transición de opacity:1a opacity:0y luego cambio display:blocka display:noneque debería ser capaz de hacerlo.
Jens
57

En lugar de devoluciones de llamada, que no existen en CSS, podemos usar la transition-delaypropiedad.

#selector {
    overflow: hidden; // Hide the element content, while height = 0
    height: 0; opacity: 0;
    transition: height 0ms 400ms, opacity 400ms 0ms;
}
#selector.visible {
    height: auto; opacity: 1;
    transition: height 0ms 0ms, opacity 600ms 0ms;
}

Entonces, ¿qué está pasando aquí?

  1. Cuando visiblese agrega la clase, ambos heighte opacityinician la animación sin demora (0 ms), aunque heighttarda 0 ms en completar la animación (equivalente a display: block) y opacity600 ms.

  2. Cuando visiblese elimina la clase, opacitycomienza la animación (retraso de 0 ms, duración de 400 ms), y la altura espera 400 ms y solo entonces instantáneamente (0 ms) restaura el valor inicial (equivalente a display: noneen la devolución de llamada de animación).

Tenga en cuenta que este enfoque es mejor que los que usan visibility. En tales casos, el elemento todavía ocupa el espacio en la página, y no siempre es adecuado.

Para más ejemplos, consulte este artículo .

Webars
fuente
8
Sorprende que esto no tenga más votos positivos: esta solución inteligente de alguna manera funciona perfectamente.
fanfarria
3
Solo funciona con lo height:100%que puede destruir el diseño en algunos casos. Gran solución, si eso no es un problema. Uno de los pocos bidireccionales de trabajo.
Fabian von Ellerts
¡Esto es genial! Pude simplificarlo aún más en mi caso porque quería que el tiempo de animación de desvanecimiento fuera el mismo que el tiempo de desvanecimiento. En lugar de usar transition: height 0ms 0ms, opacity 600ms 0ms, acabo de usar transition-delay: 0s.
Jacob Lockard
52

display no es una de las propiedades sobre las que funciona la transición.

Consulte Propiedades CSS animables para ver la lista de propiedades CSS que pueden tener transiciones aplicadas. Consulte el Módulo 4 de valores y unidades CSS, Combinación de valores: interpolación, adición y acumulación para ver cómo se interpolan.

Hasta CSS 3 figuraba en 9.1. Propiedades de CSS (solo cierre la ventana emergente de advertencia)

También intenté usar la altura, pero eso simplemente falló miserablemente.

La última vez que tuve que hacer esto, utilicé en su max-heightlugar, que es una propiedad animable (aunque fue un truco, funcionó), pero tenga en cuenta que puede ser muy molesto para páginas complejas o usuarios con dispositivos móviles de gama baja. dispositivos.

robocat
fuente
El enlace ya no apunta directamente a la sección de propiedades animables.
Andrew Lam
1
@AndrewLam fijo. Gracias.
robocat
34

Puede agregar una animación personalizada a la propiedad de bloque ahora.

@keyframes showNav {
  from {opacity: 0;}
  to {opacity: 1;}
}
.subnav-is-opened .main-nav__secondary-nav {
  display: block;
  animation: showNav 250ms ease-in-out both;
}

Manifestación

En esta demostración los cambios sub-menú de display:nonea display:blocky se las arregla para desvanecerse.

Manish Pradhan
fuente
Debería myfirstestar showNavaquí? ¿Y qué hay de Firefox? Lamentablemente, no puedo encontrar algo relacionado en la página de demostración que está mencionando.
Sebastian vom Meer
Gracias @SebastianG. Hice la corrección y agregué más información arriba.
Manish Pradhan
@Sebastian vom Meer: las versiones anteriores de Firefox necesitan el prefijo de vendedor "-moz-", vea el código editado
Herr_Schwabullek
23
A menos que me falte algo, el enlace "demo" ya no muestra una transición de submenú.
Realista
1
La transición correcta para algo que no ocupa espacio (como es el caso con display: none) a algo que ocupa espacio (display: block), es una expansión de dimensiones, no un desvanecimiento. Si se trata de una visibilidad oculta (que ocupa espacio pero no es visible) a visible, entonces el tamaño permanece igual y es apropiado un desvanecimiento gradual. Ninguna de estas respuestas resuelve el problema.
Triynko
21

Según el borrador de trabajo del W3C, el 19 de noviembre de 2013 display no es una propiedad animable . Afortunadamente, visibilityes animable. Puede encadenar su transición con una transición de opacidad ( JSFiddle ):

  • HTML:

    <a href="http://example.com" id="foo">Foo</a>
    <button id="hide-button">Hide</button>
    <button id="show-button">Show</button>
  • CSS:

    #foo {
        transition-property: visibility, opacity;
        transition-duration: 0s, 1s;
    }
    
    #foo.hidden {
        opacity: 0;
        visibility: hidden;
        transition-property: opacity, visibility;
        transition-duration: 1s, 0s;
        transition-delay: 0s, 1s;
    }
  • JavaScript para probar:

    var foo = document.getElementById('foo');
    
    document.getElementById('hide-button').onclick = function () {
        foo.className = 'hidden';
    };
    
    document.getElementById('show-button').onclick = function () {
        foo.className = '';
    };

Tenga en cuenta que si solo hace que el enlace sea transparente, sin configuración visibility: hidden, entonces se podrá hacer clic en él.

feklee
fuente
1
no veo ninguna transición en absoluto en el ejemplo jsfiddle
Gino
@Gino Lo arreglé cambiando 0a 0s. Aparentemente, en el pasado el navegador que usaba para probar soportaba cero unidades sin unidad. Sin embargo, los tiempos sin unidades no forman parte de la Recomendación del candidato del W3C, 29 de septiembre de 2016 .
feklee
Esto debería ser votado más. Me pareció una de las mejores soluciones.
Arthur Tarasov
El uso del retraso es la mejor lógica para entender cómo funciona esto. Animar y esconderse. Mostrar y animar. La solución perfecta para mí
Bruno Pitteli Gonçalves
15

Editar: mostrar ninguno no se aplica en este ejemplo.

@keyframes hide {
  0% {
    display: block;
    opacity: 1;
  }
  99% {
    display: block;
  }
  100% {
    display: none;
    opacity: 0;
  }
}

Lo que sucede arriba es que hasta el 99% de la visualización de la animación está configurada para bloquear mientras la opacidad se desvanece. En el último momento, la propiedad de visualización se establece en none.

Y lo más importante es retener el último cuadro después de que la animación termine usando el modo de relleno de animación: adelante

.hide {
   animation: hide 1s linear;
   animation-fill-mode: forwards;
}

Aquí hay dos ejemplos: https://jsfiddle.net/qwnz9tqg/3/

Pawel
fuente
con esta solución display: noneno funciona entonces?
Bart Calixto
Debería ser 'modo de relleno de animación: reenvíos' no 'reenviar'
Alex
Las ediciones deben tener seis caracteres y solo puedo sugerir editar, de ahí el comentario
Alex
1
@Alex Ok. Error tipográfico corregido. Lo interesante es que mi respuesta ni siquiera funciona como se esperaba, pero es lógico pensar que sí, así que la guardaré hasta que los navegadores lo admitan.
Pawel
3
Como puede ver en este violín con cursores display: none, nunca se implementa realmente. jsfiddle.net/3e6sduqh
robstarbuck
14

¡Mi truco ordenado de JavaScript es separar todo el escenario en dos funciones diferentes !

Para preparar las cosas, se declara una variable global y se define un controlador de eventos:

  var tTimeout;
  element.addEventListener("transitionend", afterTransition, true);//firefox
  element.addEventListener("webkitTransitionEnd", afterTransition, true);//chrome

Luego, cuando oculto el elemento, uso algo como esto:

function hide(){
  element.style.opacity = 0;
}

function afterTransition(){
  element.style.display = 'none';
}

Para reaparecer el elemento, estoy haciendo algo como esto:

function show(){
  element.style.display = 'block';
  tTimeout = setTimeout(timeoutShow, 100);
}

function timeoutShow(){
  element.style.opacity = 1;
}

Funciona, hasta ahora!

Sasa Milenkovic
fuente
Esto funciona debido al retraso de tiempo y no porque los comandos de JavaScript están en funciones separadas. Los retrasos en el tiempo parecen ser una buena solución en muchos casos :)
10

Me encontré con esto hoy, con un position: fixedmodal que estaba reutilizando. No pude mantenerlo display: noney luego animarlo, ya que simplemente apareció y z-index(valores negativos, etc.) también hicieron cosas raras.

También estaba usando un height: 0to height: 100%, pero solo funcionó cuando apareció el modal. Esto es lo mismo que si usaras left: -100%o algo así.

Entonces me di cuenta de que había una respuesta simple. Et voila:

Primero, tu modal oculto. Observe el heightes 0y revise la heightdeclaración en las transiciones ... tiene un 500ms, que es más largo que mi opacitytransición . Recuerde, esto afecta la transición de desvanecimiento de salida: devolver el modal a su estado predeterminado.

#modal-overlay {
    background: #999;
    background: rgba(33,33,33,.2);
    display: block;
    overflow: hidden;
    height: 0;
    width: 100%;
    position: fixed;
    top: 0;
    left: 0;
    opacity: 0;
    z-index: 1;
    -webkit-transition: height 0s 500ms, opacity 300ms ease-in-out;
       -moz-transition: height 0s 500ms, opacity 300ms ease-in-out;
            -ms-transition: height 0s 500ms, opacity 300ms ease-in-out;
         -o-transition: height 0s 500ms, opacity 300ms ease-in-out;
        transition: height 0s 500ms, opacity 300ms ease-in-out;
}

Segundo, tu modal visible. Digamos que está configurando un .modal-activea body. Ahora el heightes 100%, y mi transición también ha cambiado. Quiero heightque se cambie al instante y opacityque se tome 300ms.

.modal-active #modal-overlay {
    height: 100%;
    opacity: 1;
    z-index: 90000;
    -webkit-transition: height 0s, opacity 300ms ease-in-out;
       -moz-transition: height 0s, opacity 300ms ease-in-out;
        -ms-transition: height 0s, opacity 300ms ease-in-out;
         -o-transition: height 0s, opacity 300ms ease-in-out;
            transition: height 0s, opacity 300ms ease-in-out;
}

Eso es todo, funciona como un encanto.

Hotmeteor
fuente
Votación por su estilo de alinear las propiedades prefijadas del proveedor vend
George Green
9

Tomando algunas de estas respuestas y algunas sugerencias en otros lugares, lo siguiente funciona muy bien para los menús emergentes (estoy usando esto con Bootstrap  3, específicamente):

nav .dropdown-menu {
    display: block;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    transition: max-height 500ms, opacity 300ms;
    -webkit-transition: max-height 500ms, opacity 300ms;
}
nav .dropdown:hover .dropdown-menu {
    max-height: 500px;
    opacity: 1;
    transition: max-height 0, opacity 300ms;
    -webkit-transition: max-height 0, opacity 300ms;
}

También puede usar heighten lugar de max-heightsi especifica ambos valores, ya height:autoque no está permitido con transitions. El valor de desplazamiento de max-heightnecesita ser mayor que el heightdel menú.

Edyn
fuente
3
Es un buen truco, pero hay una deficiencia. El tiempo de transición depende de la altura. Si hay varios menús con altura variable, los elementos con una altura más cercana a la altura máxima se animarían muy bien. Sin embargo, los más cortos se animarían demasiado rápido, dando una experiencia inconsistente.
usuario
8

Encontré una mejor manera para este problema, puedes usar CSS Animation y hacer tu increíble efecto para mostrar elementos.

    .item {
     display: none;
}

.item:hover {
     display: block;
     animation: fade_in_show 0.5s
}

@keyframes fade_in_show {
     0% {
          opacity: 0;
          transform: scale(0)
     }
     100% {
          opacity: 1;
          transform: scale(1)
     }
}
irgfx
fuente
Gracias por este útil consejo. Eso está funcionando perfectamente.
Sedat Kumcu
6

Me he encontrado con este problema varias veces y ahora simplemente fui con:

.block {
  opacity: 1;
  transition: opacity 250ms ease;
}

.block--invisible {
  pointer-events: none;
  opacity: 0;
}

Al agregar la clase block--invisible, no se podrá hacer clic en los Elementos completos, pero todos los Elementos detrás de él serán pointer-events:nonecompatibles con todos los principales navegadores (sin IE <11).

DominikAngerer
fuente
¿Puedes poner esto en un ejemplo, por favor?
GarethAS
1
Esto funciona bien, pero debe tenerse en cuenta que los eventos de puntero no funcionan con IE por debajo de 11
user1702965
Se agregó una nota para IE por debajo de 11
DominikAngerer
Si necesita tener dicho elemento encima de todo (por ejemplo, un menú), esta es honestamente la forma más limpia de hacerlo ahora que en 2019 el soporte es sólidopointer-events
Josh Davenport
5

Cambiar overflow:hiddena overflow:visible. Funciona mejor Yo uso así:

#menu ul li ul {
    background-color:#fe1c1c;
    width:85px;
    height:0px;
    opacity:0;
    box-shadow:1px 3px 10px #000000;
    border-radius:3px;
    z-index:1;
    -webkit-transition:all 0.5s ease;
    -moz-transition:all 0.6s ease;
}

#menu ul li:hover ul  {
    overflow:visible;
    opacity:1;
    height:140px;
}

visiblees mejor porque overflow:hiddenactuar exactamente como a display:none.

Jesús
fuente
Este fue el boleto para mí. ¡Gracias!
Dan Loewenherz
5

No se requiere JavaScript, y no se necesita una altura máxima escandalosamente enorme. En su lugar, configure sus max-heightelementos de texto y use una unidad relativa de fuente como remo em. De esta forma, puede establecer una altura máxima mayor que su contenedor, evitando un retraso o "estallido" cuando se cierra el menú:

HTML

<nav>
  <input type="checkbox" />
  <ul>
    <li>Link 1</li>
    <li>Link 1</li>
    <li>Link 1</li>
    <li>Link 1</li>
  </ul>
</nav>

CSS

nav input + ul li { // Notice I set max-height on li, not ul
   max-height: 0;
}

nav input:checked + ul li {
   max-height: 3rem; // A little bigger to allow for text-wrapping - but not outrageous
}

Vea un ejemplo aquí: http://codepen.io/mindfullsilence/pen/DtzjE

silencio mental
fuente
Similar a este, puede establecer el alto de línea en su texto y transformarlo a cero. Si solo hay texto dentro, esto ocultará el contenedor.
vicmortelmans
¿Por qué tan pocos votos? Esta es una muy buena respuesta y puede ser la que yo uso.
mwilcox
@mwilcox Porque usa una altura fija, lo cual es malo cuando se trabaja con contenido dinámico (incluso una altura estimada). Y solo es útil en el contenido de texto.
Fabian von Ellerts
5

Después de que se escribió la respuesta aceptada de Guillermo, la especificación de transición CSS de 2012-04-03 cambió el comportamiento de la transición de visibilidad y ahora es posible resolver este problema de una manera más corta , sin el uso del retraso de transición:

.myclass > div {
                   transition:visibility 1s, opacity 1s;
                   visibility:hidden;  opacity:0
               }
.myclass:hover > div
               {   visibility:visible; opacity:1 }

El tiempo de ejecución especificado para ambas transiciones generalmente debe ser idéntico (aunque un tiempo un poco más largo para la visibilidad no es un problema).

Para una versión en ejecución, vea mi publicación de blog Visibilidad de transición CSS .

Escriba el título de la pregunta "Transiciones en la pantalla: propiedad" y en respuesta a los comentarios de Rui Marques y josh a la respuesta aceptada:

Esta solución funciona en casos donde es irrelevante si se utiliza la propiedad de visualización o visibilidad (como probablemente fue el caso en esta pregunta).

No eliminará completamente el elemento display:none, solo lo hará invisible, pero aún permanecerá en el flujo del documento e influirá en la posición de los siguientes elementos.

Las transiciones que eliminan por completo el elemento similar display:nonese pueden hacer usando height (como lo indican otras respuestas y comentarios), max-height o margin-top / bottom, pero también vea Cómo puedo hacer la transición de height: 0; a la altura: auto; usando CSS? y mi blog publica soluciones para transiciones CSS en las propiedades de visualización y altura .

En respuesta al comentario de GeorgeMillo: se necesitan ambas propiedades y ambas transiciones: la propiedad de opacidad se usa para crear una animación de aumento gradual y la característica de visibilidad para evitar que el elemento siga reaccionando en eventos del mouse. Se necesitan transiciones en la opacidad para el efecto visual y en la visibilidad para retrasar la ocultación hasta que finalice el desvanecimiento.

Helmut Emmelmann
fuente
4

Finalmente encontré una solución para mí, combinándome opacitycon position absolute(no ocupar espacio cuando está oculto).

.toggle {
  opacity: 0;
  position: absolute;
  transition: opacity 0.8s;
}

.parent:hover .toggle {
  opacity: 1;
  position: static;
}
Damjan Pavlica
fuente
4

Sospecho que cualquiera que esté comenzando las transiciones CSS descubre rápidamente que no funcionan si está modificando la propiedad de visualización (bloque / ninguno) al mismo tiempo. Una solución alternativa que aún no se ha mencionado es que puede seguir utilizando display:block/nonepara ocultar / mostrar el elemento, pero establecer su opacidad en 0 para que, incluso cuando lo sea display:block, siga siendo invisible.

Luego, para atenuarlo, agregue otra clase CSS como "on", que establece la opacidad en 1 y define la transición para la opacidad. Como puede haber imaginado, tendrá que usar JavaScript para agregar esa clase "on" al elemento, pero al menos todavía está usando CSS para la transición real.

PD: si se encuentra en una situación en la que necesita hacer ambas cosas display:blocky agregar la clase "on", al mismo tiempo, difiera la última utilizando setTimeout. De lo contrario, el navegador solo ve que ambas cosas suceden a la vez y desactiva la transición.

Larry Gerndt
fuente
¡Excelente, gracias! Escribí otra respuesta con un ejemplo que realmente funciona display:none.
mojuba
4

Es tan simple como lo siguiente :)

@keyframes fadeout {
    0% { opacity: 1; height: auto; }
    90% { opacity: 0; height: auto; }
    100% { opacity: 0; height: 0;
}
animation: fadeout linear 0.5s 1 normal forwards !important;

Haz que se desvanezca y luego hazlo height 0;. También asegúrese de usar los reenvíos para que permanezca en el estado final.

Veljko Simakovic
fuente
Esto se restablecería instantáneamente al eliminar la clase, por lo que no es una buena idea
Sean T
2

Esta solución tiene una excelente compatibilidad, y aún no la he visto:

.hidden-element {
  position: absolute;
  z-index: -1;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity .5s ease-out;
}

.hidden-element.visible {
  position: static;
  z-index: auto;
  pointer-events: auto;
  visibility: visible;
  opacity: 1;
}

Explicación : utiliza el visibility: hiddentruco (que es compatible con "mostrar y animar" en un solo paso), pero utiliza la combinación position: absolute; z-index: -1; pointer-events: none;para asegurarse de que el contenedor oculto no ocupe espacio y no responda a las interacciones del usuario .

Christophe Marois
fuente
1
Pero el cambio de positiontodavía hará que el elemento se mueva, ¿no?
Arsylum
Bueno, por supuesto: position: absolutesignifica que el elemento no ocupa espacio, como se describe en la explicación. Cuando cambia position: staticy aparece, ocupará espacio como un elemento normal, que es el punto de esta pregunta. ¡Te sugiero que lo intentes!
Christophe Marois
2
Yo hice. Pero si entiendo bien, la pregunta era sobre las transiciones . Como en "animaciones suaves".
Arsylum
2

Puede hacer que esto funcione de la manera natural que esperaba, usando la pantalla, pero debe acelerar el navegador para que funcione, ya sea usando Javascript o como otros han sugerido un truco elegante con una etiqueta dentro de otra. No me importa la etiqueta interna, ya que complica aún más el CSS y las dimensiones, así que aquí está la solución de Javascript:

https://jsfiddle.net/b9chris/hweyecu4/17/

Comenzando con un cuadro como:

<div id="box" class="hidden">Lorem</div>

Una caja escondida.

div.hidden {
    display: none;
}
#box {
    transition: opacity 1s;
}

Vamos a usar un truco que se encuentra en un q / a relacionado, verificando offsetHeight para estrangular el navegador instantáneamente:

https://stackoverflow.com/a/16575811/176877

Primero, una biblioteca que formaliza el truco anterior:

$.fn.noTrnsn = function () {
    return this.each(function (i, tag) {
        tag.style.transition = 'none';
    });
};
$.fn.resumeTrnsn = function () {
    return this.each(function (i, tag) {
        tag.offsetHeight;    
        tag.style.transition = null;
    });
};

A continuación, lo usaremos para revelar un cuadro y desvanecerlo en:

$('#button').on('click', function() {
    var tag = $('#box');
    if (tag.hasClass('hidden'))
        tag.noTrnsn().removeClass('hidden')
        .css({ opacity: 0 })
        .resumeTrnsn().css({ opacity: 1 });
    else
        tag.css({ opacity: 0 });
});

Esto desvanece la caja dentro y fuera. Entonces, .noTrnsn()apaga las transiciones, luego eliminamos elhidden clase, que voltea displaya partir nonede su valor por defecto, block. Luego establecemos la opacidad en 0 para prepararnos para desvanecernos. Ahora que hemos preparado el escenario, activamos las transiciones nuevamente con .resumeTrnsn(). Y finalmente, inicie la transición estableciendo la opacidad en 1.

Sin la biblioteca, tanto el cambio para mostrar como el cambio para la opacidad nos hubieran dado resultados indeseables. Si simplemente elimináramos las llamadas a la biblioteca, no obtendríamos ninguna transición.

Tenga en cuenta que lo anterior no establece la visualización en ninguno nuevamente al final de la animación de desvanecimiento. Sin embargo, podemos ser más elegantes. Hagámoslo con uno que se desvanece y crece en altura desde 0.

¡Lujoso!

https://jsfiddle.net/b9chris/hweyecu4/22/

#box {
    transition: height 1s, opacity 1s;
}

Ahora estamos haciendo la transición tanto de altura como de opacidad. Tenga en cuenta que no tenemos intención de altura, lo que significa que es el valor por defecto, auto. Convencionalmente, esto no se puede hacer la transición: pasar de automático a un valor de píxel (como 0) no conseguirá ninguna transición. Vamos a solucionar eso con la biblioteca y un método de biblioteca más:

$.fn.wait = function (time, fn) {
    if (time)
        this.delay(time);
    if (!fn)
        return this;

    var _this = this;
    return this.queue(function (n) {
        fn.call(_this);
        n();
    });
};

Este es un método conveniente que nos permite participar en la cola fx / animación existente de jQuery, sin requerir ninguno del marco de animación que ahora está excluido en jQuery 3.x. No voy a explicar cómo funciona jQuery, pero basta con decir, el .queue()y .stop()plomería que jQuery proporciona ayuda a prevenir las animaciones de pisar el uno al otro.

Animamos el efecto deslizante hacia abajo.

$('#button').on('click', function() {
    var tag = $('#box');
    if (tag.hasClass('hidden')) {
        // Open it
        // Measure it
        tag.stop().noTrnsn().removeClass('hidden').css({
            opacity: 0, height: 'auto'
        });
        var h = tag.height();
        tag.css({ height: 0 }).resumeTrnsn()
        // Animate it
        .css({ opacity: 1, height: h })
        .wait(1000, function() {
            tag.css({ height: 'auto' });
        });
    } else {
        // Close it
        // Measure it
        var h = tag.noTrnsn().height();
        tag.stop().css({ height: h })
        .resumeTrnsn()
        // Animate it
        .css({ opacity: 0, height: 0 })
        .wait(1000, function() {
            tag.addClass('hidden');
        });
    }
});

Este código comienza verificando #boxy si está actualmente oculto, verificando su clase. Pero logra más utilizando la wait()llamada a la biblioteca, al agregar elhidden clase al final de la animación de deslizamiento / desvanecimiento, que esperaría encontrar si de hecho está oculta, algo que el ejemplo más simple anterior no podría hacer. Esto también permite mostrar / ocultar el elemento una y otra vez, lo cual fue un error en el ejemplo anterior, porque la clase oculta nunca se restauró.

También puede ver los cambios de clase y CSS que se solicitan después .noTrnsn()para establecer generalmente el escenario para las animaciones, incluida la toma de medidas, como medir cuál será la altura final #boxsin mostrar eso al usuario, antes de llamar .resumeTrnsn(), y animarlo desde ese conjunto completo etapa a sus valores de CSS objetivo.

Vieja respuesta

https://jsfiddle.net/b9chris/hweyecu4/1/

Puede hacer la transición al hacer clic con:

function toggleTransition() {
  var el = $("div.box1");

  if (el.length) {
    el[0].className = "box";
    el.stop().css({maxWidth: 10000}).animate({maxWidth: 10001}, 2000, function() {
        el[0].className = "box hidden";
    });
  } else {
    el = $("div.box");
    el[0].className = "box";
    el.stop().css({maxWidth: 10001}).animate({maxWidth: 10000}, 50, function() {
        el[0].className = "box box1";
    });
  }

  return el;
}

someTag.click(toggleTransition);

El CSS es lo que adivinarías:

.hidden {
    display: none;
}
.box {
    width: 100px;
    height: 100px;
    background-color: blue;
    color: yellow;
    font-size: 18px;
    left: 20px;
    top: 20px;
    position: absolute;
    -webkit-transform-origin: 0 50%;
    transform-origin: 0 50%;
    -webkit-transform: scale(.2);
    transform: scale(.2);
    -webkit-transition: transform 2s;
    transition: transform 2s;
}
.box1{
    -webkit-transform: scale(1);
    transform: scale(1);
}

La clave es limitar la propiedad de visualización. Al eliminar la clase oculta y luego esperar 50 ms, luego comenzar la transición a través de la clase agregada, hacemos que aparezca y luego se expanda como quisiéramos, en lugar de que simplemente aparezca en la pantalla sin ninguna animación. Similar ocurre al revés, excepto que esperamos hasta que termine la animación antes de aplicar oculta.

Nota: Estoy abusando .animate(maxWidth)aquí para evitar las setTimeoutcondiciones de carrera. setTimeoutes rápido para introducir errores ocultos cuando usted u otra persona recoge el código sin darse cuenta. .animate()se puede matar fácilmente con .stop(). Solo lo estoy usando para poner un retraso de 50 ms o 2000 ms en la cola de fx estándar, donde es fácil de encontrar / resolver por otros codificadores que se basan en esto.

Chris Moschini
fuente
1

Tuve un problema similar al que no pude encontrar la respuesta. Algunas búsquedas en Google más tarde me llevaron aquí. Teniendo en cuenta que no encontré la respuesta simple que esperaba, me topé con una solución elegante y efectiva.

Resulta que la visibilitypropiedad CSS tiene un valor collapseque generalmente se usa para los elementos de la tabla. Sin embargo, si se usa en cualquier otro elemento, los convierte efectivamente en ocultos , más o menos igual, display: hiddenpero con la capacidad adicional de que el elemento no ocupa ningún espacio y aún puede animar el elemento en cuestión.

A continuación se muestra un ejemplo simple de esto en acción.

function toggleVisibility() {
  let exampleElement = document.querySelector('span');
  if (exampleElement.classList.contains('visible')) {
    return;
  }
  exampleElement.innerHTML = 'I will not take up space!';
  exampleElement.classList.toggle('hidden');
  exampleElement.classList.toggle('visible');
  setTimeout(() => {
    exampleElement.classList.toggle('visible');
    exampleElement.classList.toggle('hidden');
  }, 3000);
}
#main {
  display: flex;
  flex-direction: column;
  width: 300px;
  text-align: center;
}

.hidden {
  visibility: collapse;
  opacity: 0;
  transition: visibility 2s, opacity 2s linear;
}

.visible {
  visibility: visible;
  opacity: 1;
  transition: visibility 0.5s, opacity 0.5s linear;
}
<div id="main">
  <button onclick="toggleVisibility()">Click Me!</button>
  <span class="hidden"></span>
  <span>I will get pushed back up...</span>
</div>

Deadmano
fuente
1

La solución universal más simple para el problema es: siéntase libre de especificar display:noneen su CSS, sin embargo, tendrá que cambiarlo block(o cualquier otra cosa) usando JavaScript, y luego también tendrá que agregar una clase a su elemento en cuestión que realmente realiza la transición con setTimeout () . Eso es todo.

Es decir:

<style>
    #el {
        display: none;
        opacity: 0;
    }
    #el.auto-fade-in {
        opacity: 1;
        transition: all 1s ease-out; /* Future, future, please come sooner! */
        -webkit-transition: all 1s ease-out;
        -moz-transition: all 1s ease-out;
        -o-transition: all 1s ease-out;
    }
</style>

<div id=el>Well, well, well</div>

<script>
    var el = document.getElementById('el');
    el.style.display = 'block';
    setTimeout(function () { el.className = 'auto-fade-in' }, 0);
</script>

Esto se probó en los últimos navegadores sanos. Obviamente no debería funcionar en Internet Explorer 9 o anterior.

mojuba
fuente
1

Creo que SalmanPK tiene la respuesta más cercana. Se desvanece un elemento dentro o fuera, con las siguientes animaciones CSS. Sin embargo, la propiedad de visualización no se anima suavemente, solo la opacidad.

@-webkit-keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}

@-webkit-keyframes fadeOut {
    from { opacity: 1; }
      to { opacity: 0; }
}

Si desea animar el elemento que se mueve desde el bloque de visualización para mostrar ninguno, no puedo ver que actualmente sea posible solo con CSS. Tienes que obtener la altura y usar una animación CSS para disminuir la altura. Esto es posible con CSS como se muestra en el siguiente ejemplo, pero sería difícil saber los valores exactos de altura que necesita animar para un elemento.

jsFiddle Ejemplo de

CSS

@-webkit-keyframes pushDown {
  0% {
    height: 10em;
  }
  25% {
    height: 7.5em;
  }
  50% {
    height: 5em;
  }
  75% {
    height: 2.5em;
  }
  100% {
    height: 0em;
  }
}

.push-down {
    -webkit-animation: pushDown 2s forwards linear;
}

JavaScript

var element = document.getElementById("element");

// Push item down
element.className = element.className + " push-down";
svnm
fuente
1

Puede hacer esto con eventos de transición, por lo que crea dos clases CSS para la transición, una con la animación y otra sin mostrar el estado. ¿Y los cambias después de que termina la animación? En mi caso, puedo mostrar los divs nuevamente si presiono un botón y elimino ambas clases.

Prueba el fragmento a continuación ...

$(document).ready(function() {
  // Assign transition event
  $("table").on("animationend webkitAnimationEnd", ".visibility_switch_off", function(event) {
    // We check if this is the same animation we want
    if (event.originalEvent.animationName == "col_hide_anim") {
      // After the animation we assign this new class that basically hides the elements.
      $(this).addClass("animation-helper-display-none");
    }

  });

  $("button").click(function(event) {

    $("table tr .hide-col").toggleClass(function() {
      // We switch the animation class in a toggle fashion...
      // and we know in that after the animation end, there
      // is will the animation-helper-display-none extra
      // class, that we nee to remove, when we want to
      // show the elements again, depending on the toggle
      // state, so we create a relation between them.
      if ($(this).is(".animation-helper-display-none")) {
        // I'm toggling and there is already the above class, then
        // what we want it to show the elements , so we remove
        // both classes...
        return "visibility_switch_off animation-helper-display-none";
      }
      else {
        // Here we just want to hide the elements, so we just
        // add the animation class, the other will be added
        // later be the animationend event...
        return "visibility_switch_off";
      }
    });
  });
});
table th {
  background-color: grey;
}

table td {
  background-color: white;
  padding: 5px;
}

.animation-helper-display-none {
  display: none;
}

table tr .visibility_switch_off {
  animation-fill-mode: forwards;
  animation-name: col_hide_anim;
  animation-duration: 1s;
}

@-webkit-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@-moz-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@-o-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@keyframes col_hide_anim {
  0%   {opacity: 1;}
  100% {opacity: 0;}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <theader>
    <tr>
      <th>Name</th>
      <th class='hide-col'>Age</th>
      <th>Country</th>
    </tr>
  </theader>
  <tbody>
    <tr>
      <td>Name</td>
      <td class='hide-col'>Age</td>
      <td>Country</td>
    </tr>
  </tbody>
</table>

<button>Switch - Hide Age column with fadeout animation and display none after</button>

Miguel
fuente
0

Si está utilizando jQuery para configurar sus clases, esto funcionará al 100%:

$(document).ready(function() {
  $('button').click(function() {
    var container = $('.container');
    
    if (!container.hasClass('active')) {
      container.addClass('show').outerWidth();
      container.addClass('active');
    }
    else {
      container.removeClass('active').one('transitionend', function() {
        container.removeClass('show');
      });
    }
  });
});
.container {
  display: none;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.container.show {
  display: flex;
}
 
.container.active {
  opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button">Toggle</button>

<div class="container">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>

Por supuesto, podría usar jQuery .fadeIn()y las .fadeOut()funciones, pero la ventaja de establecer clases en su lugar es en caso de que desee realizar la transición a un valor de visualización distinto de block(como es el valor predeterminado con .fadeIn()y .fadeOut()).

Aquí estoy flexhaciendo la transición para mostrar con un agradable efecto de desvanecimiento.

ESTRELLA G
fuente
0

En lugar de usarlo display, puede almacenar el elemento 'fuera de pantalla' hasta que lo necesite, y luego establecer su posición donde lo desee y transformarlo al mismo tiempo. Sin embargo, esto plantea una gran cantidad de otros problemas de diseño, por lo que su kilometraje puede variar.

Probablemente no quiera usar de displaytodos modos, ya que desearía que el contenido fuera accesible para los lectores de pantalla, que en su mayor parte intentan obedecer las reglas de visibilidad, es decir, si no debería ser visible a simple vista, no se mostrará como contenido al agente.

Peter Mortensen
fuente
0

Simplemente puede usar la visibilidad CSS : oculto / visible en lugar de mostrar : ninguno / bloque

div {
    visibility:hidden;
    -webkit-transition: opacity 1s ease-out;
    -moz-transition: opacity 1s ease-out;
    -o-transition: opacity 1s ease-out;
    transition: opacity 1s ease-out;
    opacity: 0;
}

parent:hover > div {
    opacity: 1;
    visibility: visible;
}
Foxdanni
fuente
55
Sin embargo, esto reserva el espacio, dejando un agujero vacío. Si desea contraer un espacio, debe animar la altura o alguna otra propiedad.
Marcy Sutton
0

Comencé un proyecto de esqueleto de código abierto llamado Toggle Display Animate .

Este asistente de esqueleto le permitirá imitar fácilmente jQuery show / hide, pero con animaciones de transición CSS 3 in / out.

Utiliza conmutadores de clase para que pueda usar cualquier método CSS que desee en los elementos además de mostrar: ninguno | bloque | tabla | en línea, etc., así como otros usos alternativos que se puedan pensar.

Su propósito de diseño principal es para estados de alternancia de elementos, y admite un estado de reversión donde ocultar el objeto le permite ejecutar su fotograma clave en reversa o reproducir una animación alternativa para ocultar el elemento.

La mayor parte del marcado para el concepto en el que estoy trabajando es CSS, y realmente se usa muy poco JavaScript.

Hay una demostración aquí: http://marcnewton.co.uk/projects/toggle-display-animate/

Bagazo
fuente