Transições na propriedade de exibição CSS

1448

No momento, estou criando um menu 'mega dropdown' de CSS - basicamente, um menu suspenso apenas de CSS comum, mas que contém tipos diferentes de conteúdo.

No momento, parece que as transições CSS 3 não se aplicam à propriedade 'display' , ou seja, você não pode fazer nenhum tipo de transição de display: nonepara display: block(ou qualquer combinação).

Existe uma maneira de o menu de segunda camada do exemplo acima aparecer gradualmente quando alguém passa o mouse sobre um dos itens de menu de nível superior?

Estou ciente de que você pode usar transições na visibility:propriedade, mas não consigo pensar em uma maneira de usá-lo efetivamente.

Eu também tentei usar a altura, mas isso apenas falhou miseravelmente.

Também estou ciente de que é trivial conseguir isso usando JavaScript, mas eu queria me desafiar a usar apenas CSS, e acho que estou ficando um pouco curto.

RichardTape
fonte
A transição CSS funciona se você a aplicar à propriedade opacidade, em vez de exibir?
Paul D. Waite
13
posição: absoluta; visibilidade: oculto; é o mesmo que display: none;
Jawad
8
@ Jawad: Somente se você adicionar algo assim z-index:0também.
31412 DanMan
7
@ Jawad: É recomendável nunca usar, a visibility: hiddenmenos que você queira que os leitores de tela o leiam (enquanto os navegadores comuns não). Ele apenas define a visibilidade do elemento (como dizer opacity: 0) e ainda é selecionável, clicável e o que quer que fosse; simplesmente não é visível.
Forest Katsch
1
Não há suporte para pointer-eventsno IE 8,9,10, por isso nem sempre é ok
Steven Pribilinskiy

Respostas:

1351

Você pode concatenar duas transições ou mais e visibilityé isso que é útil dessa vez.

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>

(Não esqueça os prefixos do fornecedor da transitionpropriedade.)

Mais detalhes estão neste artigo .

Guillermo
fonte
10
Essa resposta parece menos trabalhosa que as outras e alcança o que esperaríamos da exibição: nenhum / bloco; Obrigado. Economizou-me uma tonelada de tempo.
Brendan
21
Sim, o problema com isso é que qualquer coisa por trás disso se sobrepõe, mesmo que não seja visível. I encontrado usando altura: 0 uma solução muito melhor
Josh Bedo
741
Isso é bom, mas o problema é que os elementos "visibilidade oculta" ainda ocupam espaço, enquanto "exibir nenhum" não.
Rui Marques
41
Provavelmente estou perdendo alguma coisa, mas por que você altera a visibilidade E a opacidade? A configuração da opacidade para 0 não oculta o elemento - por que você também precisa ocultar a visibilidade?
GMA
21
@GeorgeMillo se você definir apenas a opacidade, o elemento ainda estará na renderização da página (não é possível clicar em pensamento, por exemplo).
adriendenat 26/08/14
800

Você precisa ocultar o elemento por outros meios para que isso funcione.

Consegui o efeito posicionando os dois <div>absolutamente e colocando o oculto em opacity: 0.

Se você alternar a displaypropriedade de nonepara block, sua transição para outros elementos não ocorrerá.

Para contornar isso, sempre permita que o elemento seja display: block, mas oculte o elemento ajustando qualquer um destes meios:

  1. Defina heightcomo 0.
  2. Defina opacitycomo 0.
  3. Posicione o elemento fora do quadro de outro elemento que possui overflow: hidden.

Provavelmente existem mais soluções, mas você não pode executar uma transição se alternar o elemento para display: none. Por exemplo, você pode tentar algo como isto:

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

Mas isso não vai funcionar. Pela minha experiência, descobri que isso não faz nada.

Por esse motivo, você sempre precisará manter o elemento display: block- mas poderá contorná-lo fazendo algo assim:

div {
    transition: opacity 1s ease-out;
    opacity: 0;
    height: 0;
    overflow: hidden;
}
div.active {
    opacity: 1;
    height: auto;
}
Jim Jeffers
fonte
29
Obrigado Jim por uma resposta completa. Você está absolutamente certo quanto ao fato de que, se a propriedade display: mudar, todas as suas transições não funcionarão. O que é uma pena - eu me pergunto qual é o raciocínio por trás disso. Em uma nota lateral, no mesmo link que eu postei na pergunta original, você pode ver onde eu estou com ela. O único (pequeno) problema que tenho é no Chrome [5.0.375.125] quando a página é carregada. Você pode ver o menu desaparecendo rapidamente à medida que os elementos são carregados na página. Firefox 4.0b2 e Safari 5.0 são absolutamente bons ... bug ou algo que eu perdi?
21410 RichardTape
7
Eu concordo que isso esteja certo e contribuirá com isso; um alerta para futuros viajantes. Encontrei uma solução que funcionava no Chrome, mas descobri que ela falha no iPhone 4: isso visibility:hidden; opacity:0; -webkit-transition: all .2s ease-in-out;não apenas faz a transição corretamente, mas o elemento de destino nunca será exibido. O controle de qualidade falhará com você, eu e sua mãe.
simples
1
Você pode realmente fazer a transição a propriedade de visibilidade, bem .. apenas dizendo
Cu7l4ss
2
Se você definir seu estado oculto para height: 0;e não fizer a transição, a transição não funcionará. Eu tentei isso apenas tentando fazer a transição da opacidade. Eu tive que remover oheight: 0;
chovy
10
você acabou de ganhar uma votação por causa do muito overflow: hiddenobrigado!
Thiagoh
279

No momento desta publicação, todos os principais navegadores desabilitam as transições CSS se você tentar alterar a displaypropriedade, mas as animações CSS ainda funcionam bem para que possamos usá-las como uma solução alternativa.

Código de exemplo (você pode aplicá-lo ao seu menu de acordo) Demonstração :

Adicione o seguinte CSS à sua folha de estilo:

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

Em seguida, aplique a fadeInanimação à criança no foco principal (e, é claro, no conjunto display: block):

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

Atualização 2019 - Método que também suporta o desbotamento:

(É necessário algum 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
fonte
3
Obrigado por isso. O height: 0truque (para transições) mencionado acima não parece funcionar porque a altura é definida como 0 na transição de desvanecimento, mas esse truque parece funcionar muito bem.
Elliot Winkler
41
Obrigado, muito útil. Mas como desaparecer?
Illiou
1
O primeiro parágrafo desta resposta não faz muito sentido. Os navegadores não desativam todas as transições imediatamente no momento em que você usa a displaypropriedade - não há realmente nenhuma razão para isso. E mesmo que eles fizeram , por que animações trabalhar então? Você também não pode usar a displaypropriedade nas animações CSS.
BoltClock
7
Melhor resposta, não é possível com transições, mas sim com animações.
Mikel
6
Que tal animação reversa, que é quando o elemento deve desaparecer lentamente?
Verde
77

Eu suspeito que o razão qual as transições sejam desabilitadas se displayalteradas seja por causa do que a exibição realmente faz. Ele não mudar qualquer coisa que poderia concebivelmente ser bem animado.

display: none;e visibility: hidden;são duas coisas completamente diferentes.
Ambos têm o efeito de tornar o elemento invisível, mas com visibility: hidden;ele ainda é renderizado no layout, mas não de forma visível .
O elemento oculto ainda ocupa espaço e ainda é renderizado em linha ou como um bloco ou bloco em linha ou tabela ou o que o displayelemento solicitar que ele renderize como e ocupa espaço em conformidade.
Outros elementos não se movem automaticamente para ocupar esse espaço. O elemento oculto simplesmente não renderiza seus pixels reais para a saída.

display: nonepor outro lado, na verdade, impede que o elemento seja renderizado inteiramente .
Não ocupa nenhum espaço de layout.
Outros elementos que ocupariam parte ou todo o espaço ocupado por esse elemento agora se ajustam para ocupar esse espaço, como se o elemento simplesmente não existisse .

displaynão é apenas outro atributo visual.
Estabelece todo o modo de renderização do elemento, como se é um block, inline, inline-block, table, table-row, table-cell, list-item, ou qualquer outra coisa!
Cada um deles tem ramificações de layout muito diferentes, e não haveria maneira razoável de animar ou fazer a transição suave (tente imaginar uma transição suave de blockpara inlineou vice-versa, por exemplo!).

É por isso que as transições são desativadas se a exibição é alterada (mesmo que a mudança seja para ou de none- nonenão é apenas invisibilidade, é o seu próprio modo de renderização de elemento que significa que não é renderizada!).

Joel_MMCC
fonte
8
Por mais boas que sejam as soluções acima, foi muito satisfatório obter uma explicação sensata sobre o motivo pelo qual as transições não se aplicam aos atributos de exibição.
kqr
8
Discordo. Poderia fazer total sentido. Se o bloco display: none to display: ocorreu instantaneamente no início da transição, isso seria ótimo. E para voltar à transição, se fosse de display: block para display: nenhum no final da transição, seria perfeito.
Curtis Yallop
2
Se você não consegue imaginar como é uma animação de um ponto a um retângulo, você tem problemas. Uma transição de não ocupar espaço para ocupar um espaço retangular é MUITO óbvia, tipo, você faz isso toda vez que arrasta um retângulo com o mouse, como em todos os aplicativos de todos os tempos. Como resultado dessa falha, existem muitos hacks envolvendo margens máximas e negativas, e isso é ridículo. A única coisa que funciona é armazenar em cache a altura 'real' e, em seguida, animar de zero para o valor em cache com JavaScript. Triste.
Triynko
2
Tryniko: display: none não altera o tamanho do elemento para um retângulo de 0 x 0 - ele o remove do DOM. Você poderia animar de um retângulo para um ponto animando as propriedades de largura e altura para zero e, em seguida, os outros elementos fluiriam ao redor dele como se tivesse 'display: none', mas seu atributo 'display' permaneceria 'block'; o elemento ainda está no DOM. O conceito de animação entre exibição: bloco e exibição: nenhum é ridículo: não há estados intermediários. Um elemento existe no DOM ou não, não importa quão pequeno ou invisível seja.
Steve Thorpe
1
Não é ridículo animar propriedades discretas. Propriedades discretas como displaynão podem ser transferidas suavemente como outras propriedades, mas isso não significa que o tempo não seja importante. Ser capaz de controlar quando a transição de disply:blockpara display:noneacontece é muito importante. Assim como @CurtisYallop descreveu. Se eu quiser fazer a transição de opacity:1para opacity:0e depois mudar display:blockpara display:none, devo poder fazê-lo.
Jens
56

Em vez de retornos de chamada, que não existem no CSS, podemos usar a transition-delaypropriedade

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

Então, o que está acontecendo aqui?

  1. Quando a visibleclasse é adicionada, ambos heighte opacityiniciam a animação sem demora (0 ms), mas heightlevam 0 ms para concluir a animação (equivalente a display: block) e opacitylevam 600 ms.

  2. Quando a visibleclasse é removida, opacityinicia a animação (atraso de 0 ms, duração de 400 ms) e a altura espera 400 ms e somente então instantaneamente (0 ms) restaura o valor inicial (equivalente display: noneao retorno de chamada da animação).

Observe que essa abordagem é melhor do que as que estão usando visibility. Nesses casos, o elemento ainda ocupa o espaço na página e nem sempre é adequado.

Para mais exemplos, consulte este artigo .

Webars
fonte
8
surpreso que isso não tenha mais votos positivos - esta solução inteligente de alguma forma funciona perfeitamente.
fanfarra
3
Ele só funciona com o height:100%qual pode destruir o layout em alguns casos. Ótima solução, se isso não for um problema. Um dos poucos trabalhos bidirecionais.
Fabian von Ellerts
Isso é ótimo! Consegui simplificá-lo ainda mais no meu caso, porque queria que o tempo de animação fade-out fosse o mesmo que o tempo de fade-in. Em vez de usar transition: height 0ms 0ms, opacity 600ms 0ms, eu apenas usei transition-delay: 0s.
Jacob Lockard
52

display não é uma das propriedades em que a transição funciona.

Consulte Propriedades CSS animativas para obter a lista de propriedades CSS que podem ter transições aplicadas a elas. Consulte Módulo 4 de valores e unidades de CSS, combinando valores: interpolação, adição e acumulação para saber como eles são interpolados.

Até CSS 3 foi listado em 9.1. Propriedades do CSS (feche o pop-up de aviso)

Eu também tentei usar a altura, mas isso apenas falhou miseravelmente.

Na última vez em que fiz isso, usei max-height, que é uma propriedade que pode ser animada (embora tenha sido um pouco hackeada, funcionou), mas cuidado com o fato de que pode ser muito instável para páginas complexas ou usuários com dispositivos móveis de última geração dispositivos.

robocat
fonte
O link não está mais apontando diretamente para a seção de propriedades animáveis.
Andrew Lam
1
@AndrewLam Fixed. Obrigado.
precisa saber é
34

Você pode adicionar uma animação personalizada à propriedade block agora.

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

Demo

Nesta demonstração, o submenu muda de display:nonepara display:blocke ainda consegue desaparecer.

Manish Pradhan
fonte
Deveria myfirstestar showNavaqui? E o Firefox? Infelizmente, não consigo encontrar algo relacionado na página de demonstração que você está mencionando.
Sebastian vom Meer
Obrigado @SebastianG. Fiz a correção e adicionei mais algumas informações acima.
Manish Pradhan
Vom @Sebastian Meer: versões mais antigas do Firefox precisam do "-moz-" vendor prefixo, consulte código editado
Herr_Schwabullek
23
A menos que esteja faltando alguma coisa, o link "demo" não mostra mais uma transição do submenu.
Realista
1
A transição correta para algo que não ocupa espaço (como é o caso da tela: none) para algo que ocupa espaço (tela: bloco) é uma expansão de dimensões, não um desvanecimento. Se fosse uma questão de visibilidade oculta (que ocupa espaço, mas não é visível) para visível, o tamanho permanece o mesmo e um desvanecimento é apropriado. Nenhuma dessas respostas resolve o problema.
Triynko
21

De acordo com W3C Working Draft de 19 de novembro de 2013, display não é uma propriedade que pode ser animada . Felizmente, visibilityé animador. Você pode encadear sua transição com uma transição de opacidade ( 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 teste:

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

Observe que, se você apenas tornar o link transparente, sem definir visibility: hidden, ele permanecerá clicável.

feklee
fonte
1
eu não vejo nenhuma transição no exemplo do jsfiddle
Gino
@ Gino Corrigi-o mudando 0para 0s. Aparentemente, no passado, o navegador que eu usava para testar suportava zero tempo sem unidade. No entanto, horários sem unidade não fazem parte da Recomendação de Candidato do W3C, 29 de setembro de 2016 .
feklee
Isso deve ser mais votado. Eu achei que era uma das melhores soluções.
Arthur Tarasov
O uso do atraso é apenas a melhor lógica para entender como isso funciona. Animar e ocultar. Exibir e animar. A solução perfeita para mim
de Bruno Pitteli Gonçalves
15

Edit: display none não está sendo aplicado neste exemplo.

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

O que está acontecendo acima é que, através de 99% da exibição da animação, é definido um bloqueio enquanto a opacidade diminui. No último momento, a propriedade de exibição está definida como nenhuma.

E o mais importante é reter o último quadro após o término da animação, usando o modo de preenchimento da animação:

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

Aqui estão dois exemplos: https://jsfiddle.net/qwnz9tqg/3/

Pawel
fonte
com esta solução display: nonenão funciona então?
Bart Calixto
Deve ser 'animação-fill-mode: para a frente' não 'para frente'
Alex
Edições tem que ser seis caracteres e eu posso apenas sugerir editar daí o comentário
Alex
1
@Alex Ok. Erro de digitação corrigido. O interessante é que minha resposta nem funciona como o esperado, mas é lógico pensar que funcionaria, então vou mantê-la até que os navegadores o suportem.
Pawel
3
Como você pode ver neste violino com o conjunto de cursores,, display: nonenunca é realmente implementado. jsfiddle.net/3e6sduqh
robstarbuck
14

Meu puro truque de JavaScript é separar todo o cenário em duas funções diferentes !

Para preparar as coisas, uma variável global é declarada e um manipulador de eventos é definido:

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

Então, ao ocultar o elemento, eu uso algo assim:

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

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

Para reaparecer o elemento, estou fazendo algo assim:

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

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

Funciona até agora!

Sasa Milenkovic
fonte
Isso funciona devido ao atraso de tempo e não porque os comandos javascript estão em funções separadas. Atrasos parece ser uma boa solução em muitos casos :)
10

Encontrei isso hoje, com um position: fixedmodal que estava reutilizando. Eu não pude mantê-lo display: nonee depois animá-lo, pois ele apareceu rapidamente e z-index(valores negativos, etc.) também fizeram coisas estranhas.

Eu também estava usando um height: 0para height: 100%, mas só funcionou quando o modal apareceu. É o mesmo que se você usasse left: -100%ou algo assim.

Então me ocorreu que havia uma resposta simples. Et voila:

Primeiro, o seu modal oculto. Observe o heighté 0e confira a heightdeclaração nas transições ... ele tem um 500ms, que é mais longo que a minha opacitytransição . Lembre-se de que isso afeta a transição de desaparecimento gradual: retornando o modal ao seu estado padrão.

#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, seu modal visível. Digamos que você esteja definindo um .modal-activepara o body. Agora o heighté 100%, e minha transição também mudou. Eu quero heightque isso seja instantaneamente alterado e opacityque você aceite 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;
}

É isso, funciona como um encanto.

hotmeteor
fonte
Voto positivo para seu estilo de alinhar as propriedades prefixadas pelo fornecedor
George
9

Tomando algumas dessas respostas e algumas sugestões em outros lugares, o seguinte funciona muito bem para menus suspensos (estou usando isso com o Bootstrap  3, especificamente):

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

Você também pode usar heightno lugar de max-heightse especificar os dois valores, pois height:autonão é permitido com transitions. O valor de foco de max-heightnecessidades precisa ser maior que o heightdo menu pode ser possível.

Edyn
fonte
3
É um bom truque, mas há uma falha. O tempo de transição depende da altura. Se houver vários menus com altura variável, os elementos com altura mais próxima da altura máxima serão animados com animação. No entanto, os mais curtos animavam muito rápido, dando uma experiência inconsistente.
usuário
8

Encontrei uma maneira melhor para esse problema. Você pode usar a animação CSS e criar um efeito incrível ao mostrar itens.

    .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
fonte
Obrigado por esta dica útil. Isso está funcionando perfeitamente.
Sedat Kumcu
6

Eu me deparei com esse problema várias vezes e agora simplesmente acompanhava:

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

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

Ao adicionar a classe, block--invisibleo Elements inteiro não poderá ser clicado, mas todos os elementos por trás dele serão pointer-events:nonesuportados por todos os principais navegadores (sem IE <11).

DominikAngerer
fonte
Você pode colocar isso em um exemplo, por favor?
precisa saber é o seguinte
1
Isso funciona bem, mas deve-se notar que um ponteiro de eventos não funciona com IE inferior a 11
user1702965
observação adicionada para o IE abaixo de 11
DominikAngerer 4/18
Se você precisar ter esse elemento em cima de tudo (por exemplo, um menu), essa é honestamente a maneira mais limpa de fazer isso agora que, em 2019, o suporte é sólido parapointer-events
Josh Davenport
5

Mude overflow:hiddenpara overflow:visible. Funciona melhor. Eu uso assim:

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

visibleé melhor porque overflow:hiddenage exatamente como a display:none.

Jesus
fonte
Este foi o bilhete para mim. Obrigado!
Dan Loewenherz
5

O JavaScript não é necessário e nenhuma altura máxima escandalosamente enorme é necessária. Em vez disso, defina seus max-heightelementos de texto e use uma unidade relativa à fonte, como remou em. Dessa forma, você pode definir uma altura máxima maior que o seu contêiner, evitando um atraso ou "popping" quando o menu for fechado:

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
}

Veja um exemplo aqui: http://codepen.io/mindfullsilence/pen/DtzjE

mindfullsilence
fonte
Semelhante a este, você pode definir a altura da linha no seu texto e transformá-lo em zero. Se houver apenas texto dentro, isso ocultará o contêiner.
vicmortelmans
Por que tão poucos votos? Esta é uma resposta muito boa e pode ser a que eu uso.
Mwilcox
@mwilcox Porque usa uma altura fixa, o que é ruim quando se trabalha com conteúdo dinâmico (até uma altura estimada). E é útil apenas no conteúdo de texto.
Fabian von Ellerts
5

Depois que a resposta aceita de Guillermo foi escrita, a especificação de transição CSS de 03/04/2012 mudou o comportamento da transição de visibilidade e agora é possível resolver esse problema de maneira mais curta , sem o uso de atraso de transição:

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

O tempo de execução especificado para ambas as transições geralmente deve ser idêntico (embora um tempo um pouco mais longo para visibilidade não seja um problema).

Para uma versão em execução, consulte minha postagem no blog CSS Transition Visibility .

Escreva o título da pergunta "Transições na tela: propriedade" e em resposta aos comentários de Rui Marques e Jos à resposta aceita:

Essa solução funciona nos casos em que é irrelevante se a propriedade de exibição ou visibilidade for usada (como provavelmente foi o caso nesta pergunta).

Ele não remove completamente o elemento display:none, apenas o torna invisível, mas ainda permanece no fluxo do documento e influencia a posição dos seguintes elementos.

Transições que removem completamente o elemento semelhante display:nonepodem ser feitas usando height (conforme indicado por outras respostas e comentários), max-height ou margin-top / bottom, mas também consulte Como posso fazer a transição de height: 0; à altura: auto; usando CSS? e meu post no blog Soluções alternativas para transições CSS nas propriedades Display e Height .

Em resposta ao comentário de GeorgeMillo: Ambas as propriedades e as duas transições são necessárias: A propriedade opacity é usada para criar uma animação de entrada e saída e a propriedade de visibilidade para evitar que o elemento ainda reaja aos eventos do mouse. São necessárias transições na opacidade para o efeito visual e na visibilidade para atrasar a ocultação até que o fade-out esteja concluído.

Helmut Emmelmann
fonte
4

Finalmente encontrei uma solução para mim, combinando opacitycom position absolute(para não ocupar espaço quando oculto).

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

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

Eu suspeito que alguém que está iniciando transições CSS descobre rapidamente que elas não funcionam se você estiver modificando a propriedade display (block / none) ao mesmo tempo. Uma solução alternativa que ainda não foi mencionada é que você pode continuar a usar display:block/nonepara ocultar / mostrar o elemento, mas defina sua opacidade como 0 para que, mesmo quando estiverdisplay:block , ainda esteja invisível.

Em seguida, para incorporá-lo, adicione outra classe CSS, como "on", que define a opacidade como 1 e define a transição para a opacidade. Como você pode imaginar, você precisará usar JavaScript para adicionar essa classe "on" ao elemento, mas pelo menos você ainda está usando CSS para a transição real.

PS Se você se encontrar em uma situação em que precisa fazer as duas coisas display:blocke adicionar a classe "on", ao mesmo tempo, adie a última usando setTimeout. Caso contrário, o navegador apenas vê as duas coisas acontecendo ao mesmo tempo e desativa a transição.

Larry Gerndt
fonte
Excelente, obrigado! Escrevi outra resposta com um exemplo que realmente funciona display:none.
Mojuba
4

É tão simples como o seguinte :)

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

Faça com que desapareça e depois faça height 0;. Também certifique-se de usar os encaminhamentos para que eles permaneçam no estado final.

Veljko Simakovic
fonte
Isso redefinir instantaneamente ao remover a classe, então não é uma boa idéia
Sean T
2

Esta solução tem excelente compatibilidade e ainda não a vi:

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

Explicação : ele usa o visibility: hiddentruque (que é compatível com "mostrar e animar" em uma etapa), mas usa a combinação position: absolute; z-index: -1; pointer-events: none;para garantir que o contêiner oculto não ocupe espaço e não responda às interações do usuário .

Christophe Marois
fonte
1
Mas a mudança de positionstill fará o elemento estremecer, não?
Arsylum
Bem, é claro: position: absolutesignifica que o elemento não ocupa espaço, como descrito na explicação. Quando ele muda para position: statice aparece, ele ocupa espaço como um elemento normal, que é o objetivo desta pergunta. Eu sugiro que você experimente isso!
Christophe Marois
2
Eu fiz. Mas se eu entendi direito, a pergunta era sobre transições . Como em "animações suaves".
Arsylum
2

Você pode fazer com que isso funcione da maneira natural que você esperava - usando a exibição - mas você precisa acelerar o navegador para fazê-lo funcionar, usando Javascript ou outros sugeriram um truque sofisticado com uma tag dentro de outra. Não ligo para a tag interna, pois ela complica ainda mais o CSS e as dimensões, então aqui está a solução Javascript:

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

Começando com uma caixa como:

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

Uma caixa escondida.

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

Vamos usar um truque encontrado em um q / a relacionado, verificando offsetHeight para acelerar o navegador instantaneamente:

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

Primeiro, uma biblioteca que formaliza o truque acima:

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

Em seguida, vamos usá-lo para revelar uma caixa e inseri-la em:

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

Isso desbota e desbota a caixa. Assim,.noTrnsn() desliga transições, então remover a hiddenclasse, que vira displaya partir nonede seu padrão, block. Em seguida, definimos a opacidade como 0 para nos prepararmos para o desbotamento. Agora que montamos o cenário, ativamos as transições novamente .resumeTrnsn(). E, finalmente, inicie a transição configurando a opacidade para 1.

Sem a biblioteca, tanto a alteração a ser exibida quanto a opacidade teriam obtido resultados indesejáveis. Se simplesmente removêssemos as chamadas da biblioteca, não teríamos nenhuma transição.

Observe que as opções acima não definem a exibição para nenhuma novamente no final da animação fadeout. Podemos ficar mais extravagantes. Vamos fazer isso com um que desbota e cresce em altura a partir de 0.

Chique!

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

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

Agora estamos fazendo a transição de altura e opacidade. Observe que não estamos definindo a altura, o que significa que é o padrão auto,. Convencionalmente, não é possível fazer a transição - passar de automático para um valor de pixel (como 0) não fará a transição. Vamos contornar isso com a biblioteca e mais um método de biblioteca:

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

Esse é um método de conveniência que nos permite participar da fila de animação / animação existente do jQuery, sem exigir nenhuma estrutura de animação que agora está excluída no jQuery 3.x. Não vou explicar como o jQuery funciona, mas basta dizer que o .queue()e.stop() encanamento que jQuery fornece ajuda-nos a prevenir nossas animações de pisar no outro.

Vamos animar o efeito deslizar para baixo.

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

Esse código começa verificando #boxe se está oculto no momento, verificando sua classe. Mas realiza mais usando a wait()chamada de biblioteca, adicionando ohidden classe no final da animação slideout / fade, que você esperaria encontrar se estivesse de fato oculta - algo que o exemplo mais simples acima não poderia fazer. Isso também permite exibir / ocultar o elemento repetidamente, o que foi um erro no exemplo anterior, porque a classe oculta nunca foi restaurada.

Você também pode ver as alterações de CSS e de classe sendo chamadas depois .noTrnsn()para definir o cenário das animações, incluindo a tomada de medidas, como medir qual será a altura final #boxsem mostrar isso ao usuário, antes de chamar .resumeTrnsn()e animar a partir desse conjunto completo estágio para seus valores CSS de meta.

Resposta antiga

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

Você pode fazer a transição ao clicar com:

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

O CSS é o que você imagina:

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

A chave está limitando a propriedade de exibição. Ao remover a classe oculta e, em seguida, à espera de 50 ms, em seguida, iniciar a transição através da classe acrescentou, podemos obtê-lo para aparecer e, em seguida, expandir-se como queríamos, em vez de apenas blipping na tela sem qualquer animação. Similar ocorre de outra maneira, exceto que esperamos até que a animação termine antes de aplicar a ocultação.

Nota: Estou abusando .animate(maxWidth)aqui para evitar setTimeoutcondições de corrida. setTimeouté rápido para introduzir bugs ocultos quando você ou outra pessoa pega o código que não o conhece. .animate()pode ser facilmente morto com .stop(). Estou apenas usando-o para colocar um atraso de 50 ms ou 2000 ms na fila fx padrão, onde é fácil encontrar / resolver por outros codificadores construídos sobre isso.

Chris Moschini
fonte
1

Eu tive um problema semelhante ao qual não consegui encontrar a resposta. Algumas pesquisas no Google mais tarde me levaram até aqui. Considerando que não encontrei a resposta simples que esperava, encontrei uma solução elegante e eficaz.

Acontece que a visibilitypropriedade CSS tem um valor collapseque geralmente é usado para itens de tabela. No entanto, se usado em outros elementos, ele os torna efetivamente ocultos , praticamente iguais, display: hiddenmas com a capacidade adicional de que o elemento não ocupa espaço e você ainda pode animar o elemento em questão.

Abaixo está um exemplo simples disso em ação.

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

A solução universal mais simples para o problema é: sinta-se à vontade para especificar display:noneem seu CSS; no entanto, você precisará alterá-lo para block(ou qualquer outra coisa) usando JavaScript e, em seguida, também precisará adicionar uma classe ao seu elemento em questão que realmente faz a transição com setTimeout () . Isso é tudo.

Ou seja:

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

Isso foi testado nos mais recentes navegadores sãos. Obviamente, ele não deve funcionar no Internet Explorer 9 ou anterior.

mojuba
fonte
1

Eu acho que SalmanPK tem a resposta mais próxima. Ele desvanece ou desvanece um item, com as seguintes animações CSS. No entanto, a propriedade de exibição não anima suavemente, apenas a opacidade.

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

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

Se você deseja animar o elemento movendo-se do bloco de exibição para nenhum, não vejo como isso é atualmente possível apenas com CSS. Você precisa obter a altura e usar uma animação CSS para diminuir a altura. Isso é possível com o CSS, conforme mostrado no exemplo abaixo, mas seria complicado saber os valores exatos de altura que você precisa animar para um elemento.

Exemplo de jsFiddle

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

Você pode fazer isso com eventos de transição, para criar duas classes CSS para a transição, uma segurando a animação outra, mantendo o estado de exibição nenhum. E você os troca depois que a animação termina? No meu caso, posso exibir os divs novamente se pressionar um botão e remover as duas classes.

Experimente o trecho abaixo ...

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

Se você estiver usando o jQuery para definir suas classes, isso funcionará 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>

É claro que você pode usar jQuery .fadeIn()e .fadeOut()funções, mas a vantagem de definir classes é se você deseja fazer a transição para um valor de exibição diferente de block(como é o padrão com .fadeIn()e .fadeOut()).

Aqui estou fazendo a transição para exibir flexcom um bom efeito de desbotamento.

ESTRELA G
fonte
0

Em vez de usar, displayvocê pode armazenar o elemento 'fora da tela' até precisar dele e, em seguida, definir sua posição para onde você deseja e transformá-lo ao mesmo tempo. Isso traz uma série de outros problemas de design, portanto, sua milhagem pode variar.

Você provavelmente não gostaria de usá-lo de displayqualquer maneira, pois gostaria que o conteúdo fosse acessível aos leitores de tela, que, na maioria das vezes, tentam obedecer às regras de visibilidade - ou seja, se não deveriam ser visíveis aos olhos, não aparecerá como conteúdo para o agente.

Peter Mortensen
fonte
0

Você pode simplesmente usar a visibilidade do CSS : oculto / visível em vez de display : none / block

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
fonte
5
Isso reserva o espaço, porém, deixando um buraco vazio. Se você deseja recolher um espaço, é necessário animar a altura ou alguma outra propriedade.
Marcy Sutton
0

Comecei um projeto de esqueleto de código aberto chamado Toggle Display Animate .

Esse auxiliar de esqueleto permitirá que você imite facilmente o show / ocultar do jQuery, mas com animações de transição de entrada e saída de CSS 3.

Ele usa alternância de classes para que você possa usar qualquer método CSS desejado nos elementos além da exibição: none | block | table | inline, etc., bem como outros usos alternativos que podem ser pensados.

Seu principal objetivo de design é o estado de alternância do elemento e suporta um estado de reversão em que ocultar o objeto permite executar o quadro-chave em sentido inverso ou reproduzir uma animação alternativa para ocultar o elemento.

A maior parte da marcação para o conceito em que estou trabalhando é CSS, e há muito pouco JavaScript realmente usado.

Há uma demonstração aqui: http://marcnewton.co.uk/projects/toggle-display-animate/

Marc
fonte