Home > front end >  How to Animate closing dropdown with element.classList.toggle and css keyframe?
How to Animate closing dropdown with element.classList.toggle and css keyframe?

Time:05-23

I have a menu with submenu items, i managed to create a simple animation with css keyframe when opening dropdown, but i can't do the same when dropdown is closed. How can I add animation when the dropdown is closed ?

As you can see, when you close the dropdown there is no transition, it just disappears instantly.

var dropdownBtn = document.querySelectorAll('.menu-btn');
//Add this for toggling dropdown
lastOpened = null;

dropdownBtn.forEach(btn => btn.addEventListener('click', function() {
  var menuContent = this.nextElementSibling;
  menuContent.classList.toggle("show"); 
  
  //Add this for toggling dropdown
  if (lastOpened && lastOpened !== menuContent)
      lastOpened.classList.remove("show");
      lastOpened = menuContent;
}));
.menu-btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.menu-btn:hover {
  background: #000;
  color: #fff;
}


.drop_container {
   display: none;
   background-color: #017575;
   animation:animateFromBottom .3s;
}

.drop_container.show {
  display: block;
  
}

.drop_container > .item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}

@keyframes animateFromBottom {
    from{bottom:-50px;opacity:0} 
    to{bottom:0;opacity:1}
}

@keyframes animateToBottom {
  from{bottom:0;opacity:1} 
  to{bottom:-50px;opacity:0}
}
<div >

<div >One</div>
<div >
  <a  href="#">Contact Us</a>
  <a  href="#">Visit Us</a>
</div>

<div >Two</div>
<div >
  <a  href="#">Contact Us</a>
  <a  href="#">Visit Us</a>
</div>

</div>

CodePudding user response:

There are multiple ways to achieve this. Because you're showing and hiding the drop down lists with the display property we'll need to stick to animations.

Create a new class for the animateToBottom keyframes. This class should be added after an element with the show class should animate out.

Only after the "out" animation has finished should the show class be removed. With the animationend event we can see when our animation finishes so we can hide the dropdown.

const dropdownBtns = document.querySelectorAll('.menu-btn');
let lastOpened = null;

dropdownBtns.forEach(btn => btn.addEventListener('click', function() {
  const menuContent = this.nextElementSibling;

  if (lastOpened !== null) {
    const target = lastOpened;
 
    target.addEventListener('animationend', () => {
      target.classList.remove('show', 'animate-out');
 
      if (target === lastOpened) {
        lastOpened = null;
      }
    }, {
      once: true
    });

    target.classList.add('animate-out');
  }

  if (lastOpened !== menuContent) {
    menuContent.classList.add('show');
    lastOpened = menuContent;
  }
}));
.menu-btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.menu-btn:hover {
  background: #000;
  color: #fff;
}

.drop_container {
  display: none;
  background-color: #017575;
  animation: animateFromBottom .3s;
}

.drop_container.show {
  display: block;
}

.drop_container.show.animate-out {
  animation: animateToBottom .3s;
}

.drop_container>.item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}

@keyframes animateFromBottom {
  from {
    transform: translate3d(0, 10px, 0);
    opacity: 0
  }
  to {
    transform: translate3d(0, 0, 0);
    opacity: 1
  }
}

@keyframes animateToBottom {
  from {
    transform: translate3d(0, 0, 0);
    opacity: 1
  }
  to {
    transform: translate3d(0, 10px, 0);
    opacity: 0
  }
}
<div >

  <div >One</div>
  <div >
    <a  href="#">Contact Us</a>
    <a  href="#">Visit Us</a>
  </div>

  <div >Two</div>
  <div >
    <a  href="#">Contact Us</a>
    <a  href="#">Visit Us</a>
  </div>

</div>

  • Related