8

I know this question has probably been asked million times here in SO, but Ive tried most of the solution, I just dont know why it dont work for me.

So I have a dropdown with display initially targeted to none. When I click on it, it shows nice transition. (So far so good). But when I click on it again to go hide the dropdown, it hides immediately, but I dont want that. I want to hide with similar transition that was assigned to show. Here is my code for CSS:

.dropdown-menu {
  padding: 0 0;
  display: none;
  -webkit-animation: slide-down .3s ease-out;
  -moz-animation: slide-down .3s ease-out;
}

.table-dropdown-open {
  display: block;
  -webkit-animation: slide-down .3s ease-out;
  -moz-animation: slide-down .3s ease-out;
}

@-webkit-keyframes slide-down {
  0% {
    opacity: 0;
    -webkit-transform: translateY(-10%);
  }
  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
  }
}

@-moz-keyframes slide-down {
  0% {
    opacity: 0;
    -moz-transform: translateY(-10%);
  }
  100% {
    opacity: 1;
    -moz-transform: translateY(0);
  }
}

This is not a dupliacte because Im trying to give transition from block to none. Not from none to block

DingDong
  • 367
  • 3
  • 12
  • 22
  • 1
    Transitions or Animations in CSS won't work on `display: none` / `display: block`. Use `opacity: 0` / `opacity: 1` OR `visibility` property. – Mohammad Usman Feb 23 '17 at 11:32
  • I thought it would work as you can integrate from block to none. Are you sure it wont work from none to block? @MuhammadUsman – DingDong Feb 23 '17 at 11:41
  • 2
    Possible duplicate of [Transitions on the display: property](http://stackoverflow.com/questions/3331353/transitions-on-the-display-property) – sergdenisov Feb 23 '17 at 11:43
  • @DingDong Can you please give an example where the `display` property is gradually transitioned from `block` to `none`, please? I never thought it was possible. Really appreciate the effort, thank you. – tao Feb 23 '17 at 11:48
  • I think you may be right. It is highly unlikely. But how do I achieve from the transition effect by hiding dropdown? @AndreiGheorghiu – DingDong Feb 23 '17 at 12:00
  • 1
    You `display` it at all times and `transition` an `animatable` property. Such as `opacity`, from `0` to `1`. You can also play with `transform`. In effect, `transform` and `opacity` are the only 2 properties you should ever animate, as they don't require `DOM` to repaint anything other than the element you're animating, thus not hitting browser performance, even when you're animating a considerable amount of elements simultaneously. – tao Feb 23 '17 at 12:02
  • Wow, great answer. Please write an anwer below, aso that I can accept yours. You have explained performanc of browser. +1 for that @AndreiGheorghiu – DingDong Feb 23 '17 at 12:11
  • Added it as an answer, with a few extra details you should pay attention to. Happy coding! – tao Feb 23 '17 at 12:18
  • To add onto Mohammad Usman's and Andrei Gheorghiu comments, you can obtain the effects of display none by using 'position: absolute' and 'visibility: hidden'. This avoids the problems associated with elements not transitioning with 'display: none'. – HelloWorldPeace Jan 31 '19 at 12:24

2 Answers2

11

display your element at all times and only transition any animatable property. In your case, opacity looks like a good candidate, though playing with transform might also give you the desired result. Simple example:

any {
  transform: scale(0);
  transition: transform .4s cubic-bezier(.5,0,.3,1);
}
any.animated {
  transform: scale(1);
}

In effect, opacity and transform should be the only two properties you should animate, as they don't require DOM repaint on anything other than the animated element, thus not hitting browser performance, even when you're animating a considerable amount of elements simultaneously.

Please note that, even if not painted, your elements are, in effect, in the place where they would be when not transformed at all. So you might want to give them pointer-events:none when they are in the "invisible" state and pointer-events:all when they are in "visible" state, so they don't catch any pointer events while not visible.


Considering your example, I've given you two animation examples (with keyframes and without). Remember you need to prefix your code. For full browser compatibility, use > 0% in settings (the small box at the bottom).

setTimeout(function(){
  var dm = document.querySelector('.dropdown-menu');
  dm.classList.remove('hide-menu');
}, 300);
/* simple animation example, on parent. No keyframes. */
.dropdown-menu.hide-menu {
  opacity: 0;
}
.dropdown-menu {
  opacity: 1;
  transition: opacity .2s cubic-bezier(.4,0,.2,1);
  position: relative;
  animation: delay-overflow .3s;
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
}
.dropdown-menu:hover {
  animation: none;
  cursor: pointer;
}

/* animation example with keyframes, on child */

.dropdown-menu ul {
  position: absolute;
  margin-top: 0;
  padding-top: 1rem;
  top: 100%;
  opacity: 0;
  transform: translateY(-10%);
  animation: slide-up .3s;
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
}

.drowdown-menu.hide-menu ul {
  animation-duration: 0s;
}
.dropdown-menu:hover ul {
  animation: slide-down .3s;
  animation-fill-mode: forwards;
}


@keyframes slide-down {
  from {
    opacity: 0;
    transform: translateY(-10%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slide-up {
  from {
    opacity: 1;
    transform: translateY(0);
  }
  to {
    opacity: 0;
    transform: translateY(-10%);
  }
}
@keyframes delay-overflow {
  0% {
    overflow: visible;
  }
  99% {
    overflow: visible;
  }
  100% {
    overflow: hidden;
  }
}
<div class="dropdown-menu hide-menu">
  <span>Menu</span>
  <ul>
    <li>A menu item</li>
    <li>Another menu item</li>
    <li>...</li>
    <li>And so on...</li>
  </ul>
</div>

Note: A very handy trick with animation property is that it allows you to delay applying any property, even non-animatable ones from applying for the desired amount of time. I'm using this trick to delay overflow:hidden applying on the parent (and overflow is not animatable) so the animation of the child - which happens outside the bounds of the parent - is visible until the end. After it finishes, the overflow:hidden applies and it no longer catches mouse events outside the menu opener.

tao
  • 82,996
  • 16
  • 114
  • 150
  • 1
    If you're interested in animating with regard to browser performance and triggering stagger animations on collections of elements, I have recently answered [`another question`](http://stackoverflow.com/a/42261120/1891677) with a few more details on the subject. – tao Feb 23 '17 at 12:24
0

it is just simple logic, you currently have styled you slide down but you have not styled the cancellation of the div. You must first have a closed state which i believe in your case is .dropdown-menu

.dropdown-menu {
    transition: all 500ms ease;
}

or just use

.dropdown-menu {
    transition: margin 300ms cubic-bezier(0.17, 0.04, 0.03, 0.94);
}

If this does not work,

Jesse
  • 3,522
  • 6
  • 25
  • 40