147

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.

2
  • 2
    If CSS does not work as the others suggested, here's a very simple Javascript code for fading. Commented Dec 9, 2011 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
    Commented Nov 27, 2020 at 23:02

16 Answers 16

167

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;
    }
}
5
  • 22
    What about when hovering out, how to implement fadeOutToNone?
    – Green
    Commented Sep 30, 2015 at 9: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 Commented Jul 4, 2016 at 15:58
  • 2
    The -o-keyframes directive is actually useless because the first Opera version to support animations was already based on webkit. Commented Feb 23, 2018 at 12:51
  • @RicoOcepek tested on opera mini beta, it does works without the -o- thingy. Thank you! Commented Sep 4, 2019 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. caniuse.com/mdn-css_at-rules_keyframes Commented Oct 14, 2022 at 9:11
65

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;
}
3
  • 31
    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
    Commented Feb 25, 2015 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
    Commented Jan 12, 2017 at 12:11
  • 1
    You can use visibility: collapse; so the element doesn't take up space. This naturally will make it unclickable as well
    – Kaligule
    Commented Mar 28, 2023 at 7:53
44

You can do with CSS animations:

0% display:none ; opacity: 0;
1% display: block ; opacity: 0;
100% display: block ; opacity: 1;
2
  • Good idea, I succeeded keep display my element during the hover with animation-fill-mode but then the I mouseout, the element disappears. Commented Dec 9, 2011 at 20:57
  • 3
    you can use fill-mode: forwards to persist changes after the animation finishes. Commented Nov 8, 2012 at 20:05
22

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

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;
    }
    
2
  • You can simply use 0% { opacity: 0; } 100% { opacity: 1; } for the keyframes.
    – doublejosh
    Commented Feb 3, 2022 at 22:55
  • @doublejosh The solution is opacity: 0; which he has added from 0% to 20%; Best answer.
    – Rehmat
    Commented Apr 24 at 13:09
7

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.

6

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.

2
  • 6
    This doesn't play nicely with screen readers: They'll keep reading the content.
    – ehdv
    Commented Jul 23, 2013 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
    Commented Jan 12, 2015 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;
}
1
  • 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
    Commented Mar 6, 2015 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;
  }
}
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.

2
  • Unfortunately that screws up the KeePass password indicator symbol on type=password fields. Its not visible.
    – philk
    Commented Oct 19, 2015 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
    Commented Oct 30, 2015 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.

6
  • That Microsoft filter has been deprecated since IE9. Any particular reason you feel like adding it to answers in 2016?
    – TylerH
    Commented Jun 16, 2016 at 16:13
  • @TylerH How many user one is willing to reach is a question of taste. Commented Jun 16, 2016 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
    Commented Jun 16, 2016 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". Commented Jun 29, 2016 at 15:13
  • @MarcusCunningham The question is tagged with 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
    Commented Jun 30, 2016 at 11:39
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);
3
  • 2
    This question isn't tagged with JavaScript nor jQuery
    – j08691
    Commented Jan 13, 2017 at 15:27
  • I know, I wrote it to explain the reason this is happening. It was very useful for me when I learned about this and I hope it will help others too. Commented Jan 13, 2017 at 16:28
  • 3
    Btw, Opacity values are between 0 and 1
    – Amr
    Commented Apr 20, 2017 at 11:06
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>
4
  • Good idea, but then I mouseout, the element disappears… Commented Dec 9, 2011 at 20:53
  • Alexis, isn't that what you want to do? Hover means ONLY when hovering with your mouse. Please clarify what you are trying to accomplish.
    – Joshua
    Commented Dec 9, 2011 at 20:59
  • Yes, sorry. I want save the fade in the mouseout. Commented Dec 9, 2011 at 21:13
  • That changes everything. Almost. Basically what you want then is a JS function that will detect hover state, as other users have indicated, and add... well... see my updated answer.
    – Joshua
    Commented Dec 10, 2011 at 5:05
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.

-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

1
  • 2
    Does not answer the quesion, as the display property was simply removed.
    – Toast
    Commented Jul 26, 2020 at 22:03
-5

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

2
  • 7
    You've got to stop advocating jQuery everywhere dude. Commented Apr 1, 2018 at 20:24
  • 1
    @BenjaminGruenbaum jQuery is amazing man. It's great and does all things. Commented Apr 1, 2018 at 20:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.