134

I have got a problem with a CSS3 animation.

.child {
    opacity: 0;
    display: none;

    -webkit-transition: opacity 0.5s ease-in-out;
    -moz-transition: opacity 0.5s ease-in-out;
    transition: opacity 0.5s ease-in-out;
}

.parent:hover .child {
    opacity: 0.9;
    display: block;
}

This code only works if I remove the change of display.

I want to change the display just after the hover but the opacity should be changed using the transition.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Alexis Delrieu
  • 1,313
  • 2
  • 10
  • 19
  • 2
    If CSS does not work as the others suggested, [here's](http://1lifeisallwegot.wordpress.com/2011/11/03/simplest-javascript-fade-animation/) a very simple Javascript code for fading. – Abhranil Das Dec 09 '11 at 18:42
  • 3
    No one mentioned `animation-fill-mode: forwards;` So, in this case the `display` reverts back to `none` after the opacity animation runs. This CSS setting maintains the last state of the animation instead so it's `display: block` – Matthew Nov 27 '20 at 23:02

16 Answers16

156

Based on Michaels answer this is the actual CSS code to use

.parent:hover .child
{
    display: block;

    -webkit-animation: fadeInFromNone 0.5s ease-out;
    -moz-animation: fadeInFromNone 0.5s ease-out;
    -o-animation: fadeInFromNone 0.5s ease-out;
    animation: fadeInFromNone 0.5s ease-out;
}

@-webkit-keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}

@-moz-keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}

@-o-keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}

@keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}
Chris
  • 26,744
  • 48
  • 193
  • 345
  • 22
    What about when hovering out, how to implement fadeOutToNone? – Green Sep 30 '15 at 09:17
  • 5
    As you can use fractions of a percent, it's better practice to use something like 0.001% rather that 1% because it minimizes the delay to "start", which can become apparent with longer animation durations – Zach Saucier Jul 04 '16 at 15:58
  • 2
    The -o-keyframes directive is actually useless because the first Opera version to support animations was already based on webkit. – Rico Ocepek Feb 23 '18 at 12:51
  • @RicoOcepek tested on opera mini beta, it does works without the -o- thingy. Thank you! – keanu_reeves Sep 04 '19 at 22:18
  • 1
    As in 2022, the browser prefixes (-webkit-, -o-, and -moz-) are quite useless given that browsers support keyframes without prefixes since 2015. https://caniuse.com/mdn-css_at-rules_keyframes – Alexis Delrieu Oct 14 '22 at 09:11
61

If possible - use visibility instead of display

For instance:

.child {
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.3s, visibility 0.3s;
}

.parent:hover .child {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.3s, visibility 0.3s;
}
tomas.satinsky
  • 821
  • 7
  • 4
  • 29
    Problem with the visibility property is that this doesn't hide the element, it only makes it invisible. So it will still take up space. – Samuel Feb 25 '15 at 13:08
  • 9
    Not only invisible, but also transparent to events (clicks etc). Not changing display means not reflowing the document, which is a good thing. Most elements that should fade in / out through opacity should probably have fixed or absolute position anyway. – Rasmus Kaj Jan 12 '17 at 12:11
  • You can use `visibility: collapse;` so the element doesn't take up space. This naturally will make it unclickable as well – Kaligule Mar 28 '23 at 07:53
43

You can do with CSS animations:

0% display:none ; opacity: 0;
1% display: block ; opacity: 0;
100% display: block ; opacity: 1;
Kuf
  • 17,318
  • 6
  • 67
  • 91
Michael Mullany
  • 30,283
  • 6
  • 81
  • 105
20

I used this to achieve it. They fade on hover but take no space when hidden, perfect!

.child {
    height: 0px;
    opacity: 0;
    visibility: hidden;
    transition: all .5s ease-in-out;
}

.parent:hover .child {
    height: auto;
    opacity: 1;
    visibility: visible;
}
felixhirschfeld
  • 316
  • 2
  • 7
18

This workaround works:

  1. define a “keyframe”:

    @-webkit-keyframes fadeIn { 
      0% { opacity: 0; }
      20% { opacity: 0; }
      40% { opacity: 0.3; }
      60% { opacity: 0.5; }
      80% { opacity: 0.9; }
      100% { opacity: 1; }
    }
    
    @keyframes fadeIn {
      0% { opacity: 0; }
      20% { opacity: 0; }
      40% { opacity: 0.3; }
      60% { opacity: 0.5; }
      80% { opacity: 0.9; }
      100% { opacity: 1; }
    }
    
  2. Use this “keyframe” on “hover”:

    div a span { 
      display: none;
    }
    
    div a:hover span {
      display: block;
    
      -webkit-animation-name: fadeIn;
      -webkit-animation-duration: 1s;
      animation-name: fadeIn;
      animation-duration: 1s;
    }
    
Hermann Schwarz
  • 1,495
  • 1
  • 15
  • 30
6

There is another good method to get this done by using pointer-events:

.child {
    opacity: 0;
    pointer-events: none;

    -webkit-transition: opacity 0.5s ease-in-out;
    -moz-transition: opacity 0.5s ease-in-out;
    transition: opacity 0.5s ease-in-out;
}

.parent:hover .child {
    opacity: 0.9;
    pointer-events: all;
}

Unfortunately, this is not supported in IE10 and below.

RafaelKr
  • 915
  • 8
  • 18
5

I changed a bit but the result is beautiful.

.child {
    width: 0px;
    height: 0px;
    opacity: 0;
}

.parent:hover child {
    width: 150px;
    height: 300px;
    opacity: .9;
}

Thank you to everyone.

Alexis Delrieu
  • 1,313
  • 2
  • 10
  • 19
  • 6
    This doesn't play nicely with screen readers: They'll keep reading the content. – ehdv Jul 23 '13 at 15:20
  • 1
    You could add ``visibility: hidden;`` to .child / ``visibility:visible;`` to the hover and this should fix the screen reader problem – csilk Jan 12 '15 at 23:41
4

I had the same problem. I tried using animations instead of transitions - as suggested by @MichaelMullany and @Chris - but it only worked for webkit browsers even if I copy-pasted with "-moz" and "-o" prefixes.

I was able to get around the problem by using visibility instead of display. This works for me because my child element is position: absolute, so document flow isn't being affected. It might work for others too.

This is what the original code would look like using my solution:

.child {
    position: absolute;
    opacity: 0;
    visibility: hidden;

    -webkit-transition: opacity 0.5s ease-in-out;
    -moz-transition: opacity 0.5s ease-in-out;
    transition: opacity 0.5s ease-in-out;
}

.parent:hover .child {
    position: relative;
    opacity: 0.9;
    visibility: visible;
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Dave
  • 49
  • 1
  • 3
  • If you were to hover back into the child while it's animating out of view it would snap back in since the element is merely hidden away. Quite annoying if you're moving your mouse around the place. – adamj Mar 06 '15 at 22:57
3

To have animation on both ways onHoverIn/Out I did this solution. Hope it will help to someone

@keyframes fadeOutFromBlock {
  0% {
    position: relative;
    opacity: 1;
    transform: translateX(0);
  }

  90% {
    position: relative;
    opacity: 0;
    transform: translateX(0);
  }

  100% {
    position: absolute;
    opacity: 0;
    transform: translateX(-999px);
  }
}

@keyframes fadeInFromNone {
  0% {
    position: absolute;
    opacity: 0;
    transform: translateX(-999px);
  }

  1% {
    position: relative;
    opacity: 0;
    transform: translateX(0);
  }

  100% {
    position: relative;
    opacity: 1;
    transform: translateX(0);
  }
}

.drafts-content {
  position: relative;
  opacity: 1;
  transform: translateX(0);
  animation: fadeInFromNone 1s ease-in;
  will-change: opacity, transform;

  &.hide-drafts {
    position: absolute;
    opacity: 0;
    transform: translateX(-999px);
    animation: fadeOutFromBlock 0.5s ease-out;
    will-change: opacity, transform;
  }
}
tzelleke
  • 15,023
  • 5
  • 33
  • 49
Nicholas
  • 3,529
  • 2
  • 23
  • 31
2

On absolute or fixed elements you could also use z-index:

.item {
    position: absolute;
    z-index: -100;
}

.item:hover {
    z-index: 100;
}

Other elements should have a z-index between -100 and 100 now.

Luca Steeb
  • 1,795
  • 4
  • 23
  • 44
  • Unfortunately that screws up the KeePass password indicator symbol on `type=password` fields. Its not visible. – philk Oct 19 '15 at 14:31
  • 1
    Can we please stop using arbitrary z-index numbers? Here: z-index: 1; vs z-index: -1 will do just fine. Picking huge z-index numbers makes things unmanageable. – dudewad Oct 30 '15 at 22:00
2

I know, this is not really a solution for your question, because you ask for

display + opacity

My approach solves a more general question, but maybe this was the background problem that should be solved by using display in combination with opacity.

My desire was to get the Element out of the way when it is not visible. This solution does exactly that: It moves the element out of the away, and this can be used for transition:

.child {
  left: -2000px;
  opacity: 0;
  visibility: hidden;
  transition: left 0s 0.8s, visibility 0s 0.8s, opacity 0.8s;
}

.parent:hover .child {
  left: 0;
  opacity: 1;
  visibility: visible;
  transition: left 0s, visibility 0s, opacity 0.8s;
}

This code does not contain any browser prefixes or backward compatibility hacks. It just illustrates the concept how the element is moved away as it is not needed any more.

The interesting part are the two different transition definitions. When the mouse-pointer is hovering the .parent element the .child element needs to be put in place immediately and then the opacity will be changed:

transition: left 0s, visibility 0s, opacity 0.8s;

When there is no hover, or the mouse-pointer was moved off the element, one has to wait until the opacity change has finished before the element can be moved off screen:

transition: left 0s 0.8s, visibility 0s 0.8s, opacity 0.8s;

Moving the object away will be a viable alternative in a case where setting display:none would not break the layout.

I hope I hit the nail on the head for this question although I did not answer it.

  • That Microsoft filter has been deprecated since IE9. Any particular reason you feel like adding it to answers in 2016? – TylerH Jun 16 '16 at 16:13
  • @TylerH How many user one is willing to reach is a question of taste. – Hannes Morgenstern Jun 16 '16 at 20:40
  • Considering it's *deprecated*, and IE<11 is not supported by Microsoft anymore, using that property is then of questionable taste, at best. – TylerH Jun 16 '16 at 20:42
  • @TylerH It is common to have to accommodate clients who will not or cannot upgrade to a newer browser. I have a well known bank as a client who still use IE6 and refuse to upgrade for "reasons". – Marcus Cunningham Jun 29 '16 at 15:13
  • @MarcusCunningham The question is tagged with [tag:css3] which precludes the use of IE6 (and IE7 and IE8) entirely. In the earliest possible browser OP could have been writing code for, the MS filter in this answer was deprecated. And for *future* readers, it's even more useless since it's not even supported. There's *no* argument for including it in an answer on this question. It's a moot point, however, since Hannes has already removed it from his answer. – TylerH Jun 30 '16 at 11:39
  • Setting the **height: 0** on parent element won't ensure that its children is always hidden until it has **overflow: hidden** – Amr Apr 20 '17 at 11:22
2

If you are triggering the change with JS, let's say on click, there is a nice workaround.

You see, the problem happens because the animation is ignored on display:none element but browser applies all the changes at once and the element is never display:block while not animated at the same time.

The trick is to ask the browser to render the frame after changing the visibility but before triggering the animation.

Here is a JQuery example:

    $('.child').css({"display":"block"});
    //now ask the browser what is the value of the display property
    $('.child').css("display"); //this will trigger the browser to apply the change. this costs one frame render
    //now a change to opacity will trigger the animation
    $('.child').css("opacity":100);
daniel.sedlacek
  • 8,129
  • 9
  • 46
  • 77
1

One thing that I did was set the initial state's margin to be something like "margin-left: -9999px" so it does not appear on the screen, and then reset "margin-left: 0" on the hover state. Keep it "display: block" in that case. Did the trick for me :)

Edit: Save the state and not revert to previous hover state? Ok here we need JS:

<style>
.hovered { 
    /* hover styles here */
}
</style>

<script type="text/javascript">
$('.link').hover(function() {
   var $link = $(this);
   if (!$link.hasclass('hovered')) { // check to see if the class was already given
        $(this).addClass('hovered');
   } 
});
</script>
Joshua
  • 3,615
  • 1
  • 26
  • 32
0

I also had opacity transition issues in Chrome, my images / text, would show up right away with opacity 1.. when they were supposed to have opacity 0 and then transition to opacity 1...

I think the bug would be related to the fact that, the "end" class with the opacity 1 would be there almost instantaneously ( My logic was to switch from the .start class - opacity 0 - to the .end class - opacity 1 - and the transition feature was supposed to take care of it... )

this bug would only happen in Chrome.

How did I fix it: I moved the transition related CSS out of the CSS file into the STYLE tag on my page.

Weird fix huh ?

Chrome's fault.

Pablo Câmara
  • 149
  • 1
  • 5
-1

HOW TO ANIMATE OPACITY WITH CSS:
this is my code:
the CSS code

.item {   
    height:200px;
    width:200px;
    background:red;
    opacity:0;
    transition: opacity 1s ease-in-out;
}

.item:hover {
    opacity: 1;
}
code {
    background: linear-gradient(to right,#fce4ed,#ffe8cc);
}
<div class="item">

</div>
<p><code> move mouse over top of this text</code></p>

or check this demo file

function vote(){
var vote = getElementById("yourOpinion")
if(this.workWithYou):
vote += 1 };
lol

Community
  • 1
  • 1
-4

display: is not transitionable. You'll probably need to use jQuery to do what you want to do.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308