82

This is what I have jsFiddle link

nav.main ul ul {
    position: absolute;
    list-style: none;
    display: none;
    opacity: 0;
    visibility: hidden;
    padding: 10px;
    background-color: rgba(92, 91, 87, 0.9);
    -webkit-transition: opacity 600ms, visibility 600ms;
            transition: opacity 600ms, visibility 600ms;
}

nav.main ul li:hover ul {
    display: block;
    visibility: visible;
    opacity: 1;
}
<nav class="main">
    <ul>
        <li>
            <a href="">Lorem</a>
            <ul>
                <li><a href="">Ipsum</a></li>
                <li><a href="">Dolor</a></li>
                <li><a href="">Sit</a></li>
                <li><a href="">Amet</a></li>
            </ul>
        </li>
    </ul>
</nav>

Why is there no transition? If I set

nav.main ul li:hover ul {
    display: block;
    visibility: visible;
    opacity: 0; /* changed this line */
}

Then the "subnav" will never appear (of course ) but why does the transition on the opacity not trigger? How to get the transition working?

caramba
  • 21,963
  • 19
  • 86
  • 127

3 Answers3

103

As you know the display property cannot be animated BUT just by having it in your CSS it overrides the visibility and opacity transitions.

The solution...just removed the display properties.

nav.main ul ul {
  position: absolute;
  list-style: none;
  opacity: 0;
  visibility: hidden;
  padding: 10px;
  background-color: rgba(92, 91, 87, 0.9);
  -webkit-transition: opacity 600ms, visibility 600ms;
  transition: opacity 600ms, visibility 600ms;
}
nav.main ul li:hover ul {
  visibility: visible;
  opacity: 1;
}
<nav class="main">
  <ul>
    <li>
      <a href="">Lorem</a>
      <ul>
        <li><a href="">Ipsum</a>
        </li>
        <li><a href="">Dolor</a>
        </li>
        <li><a href="">Sit</a>
        </li>
        <li><a href="">Amet</a>
        </li>
      </ul>
    </li>
  </ul>
</nav>
Paulie_D
  • 107,962
  • 13
  • 142
  • 161
  • 71
    Yes, but that ul will take up the space in the dom this way. – Vcasso Aug 04 '16 at 16:28
  • 3
    Shouldn't matter because you are using absolute positioning which removed the element from normal document flow. Here is another post similar to your's ... http://stackoverflow.com/questions/22103006/css3-transition-doesnt-work-with-display-property. Remove both `display:none` and `visibility:hidden`. – Kenneth Stoddard Aug 04 '16 at 16:50
  • 9
    Yes but this causes weird clickable but invisible links. It may interrupt your other elements if they're on the same space. – Erlisar Vasquez Jul 19 '19 at 09:07
  • 3
    Also using keyboard navigation is disrupted as tabbing around will select invisible links (and thus have no visible effect to the user – confusing to say the least). – zrajm Aug 24 '19 at 15:41
  • @Vcasso for the occupied space problem, in this solution you can add max-height property and define it in the transition property, when hover out just set the max-height 0 else set to some high value and then it will not occupy the space. – Aamir Khan May 05 '23 at 10:45
84

Generally when people are trying to animate display: none what they really want is:

  1. Fade content in, and
  2. Have the item not take up space in the document when hidden

Most popular answers use visibility, which can only achieve the first goal, but luckily it's just as easy to achieve both by using position.

Since position: absolute removes the element from typing document flow spacing, you can toggle between position: absolute and position: static (global default), combined with opacity. See the below example.

.content-page {
  position:absolute;
  opacity: 0;
}

.content-page.active {
  position: static;
  opacity: 1;
  transition: opacity 1s linear;
}
AiDev
  • 1,214
  • 8
  • 11
51

You can do this with animation-keyframe rather than transition. Change your hover declaration and add the animation keyframe, you might also need to add browser prefixes for -moz- and -webkit-. See https://developer.mozilla.org/en/docs/Web/CSS/@keyframes for more detailed info.

nav.main ul ul {
    position: absolute;
    list-style: none;
    display: none;
    opacity: 0;
    visibility: hidden;
    padding: 10px;
    background-color: rgba(92, 91, 87, 0.9);
    -webkit-transition: opacity 600ms, visibility 600ms;
            transition: opacity 600ms, visibility 600ms;
}

nav.main ul li:hover ul {
    display: block;
    visibility: visible;
    opacity: 1;
    animation: fade 1s;
}

@keyframes fade {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}
<nav class="main">
    <ul>
        <li>
            <a href="">Lorem</a>
            <ul>
                <li><a href="">Ipsum</a></li>
                <li><a href="">Dolor</a></li>
                <li><a href="">Sit</a></li>
                <li><a href="">Amet</a></li>
            </ul>
        </li>
    </ul>
</nav>

Here is an update on your fiddle. https://jsfiddle.net/orax9d9u/1/

spirift
  • 2,994
  • 1
  • 13
  • 18
  • 2
    This is very clever. display:none -->block cannot animate, but adding a visibility:hidden --> visible triggers it. Coolio! – Drenai Sep 21 '17 at 11:55
  • @Drenai: where is `visibility: hidden` used? – Dan Dascalescu Nov 05 '18 at 04:16
  • @DanDascalescu I've updated the answer to include the base styles – spirift Nov 07 '18 at 11:39
  • That's the only viable answer! Playing with position `absolute` or `static` is wrong as someone else suggested! https://jsfiddle.net/centurianii/nwvyf749/ – centurian Oct 22 '19 at 15:51
  • 3
    In case you use this solution keep in mind that it works only on fade in. It won't work for fade out because display:none is applied immediately. – J. Wrong Feb 07 '21 at 04:29