Toggling the menu icon

Viewed 53

I am trying to trigger the menu icon off when clicked, and trigger on an 'x' icon. Likewise, I need the reverse of this to happen. Trigger the 'x' icon off when clicked, and the menu icon back on.

When nav-button-menuOpen is clicked, the three divs inside go from opacity: 1; to opacity: 0; and the button fades away like it's supposed to. Then the span inside nav-button-menuClose is supposed to go from opacity: 0; to opacity: 1;. I can see the class being added in the browser, but the animation on the span doesn't change the opacity property.

const mobileMenuOpenAndClose = () => {
    const menuOpenButton = document.querySelector('.nav-button-menuOpen');
    const menuCloseButton= document.querySelector('.nav-button-menuClose');
    const navMobileMenu = document.querySelector('.nav-mobileMenu');
    const navMobileMenuLinks = document.querySelectorAll('.nav-mobileMenu-links');

    menuOpenButton.addEventListener('click', () => {
      console.log('menuButtonOpen clicked');
      menuOpenButton.classList.toggle('nav-button-toggle');
      menuCloseButton.classList.toggle('nav-button-toggle');
    });

    menuCloseButton.addEventListener('click', () => {
      console.log('menuButtonClose clicked')
      menuCloseButton.classList.toggle('nav-button-toggle');
      menuOpenButton.classList.toggle('nav-button-toggle');
    });
};

mobileMenuOpenAndClose();
/* Hamburger Menu */
nav .nav-button-menuOpen {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 1;
}
nav .nav-button-menuOpen div {
  width: 30px;
  height: 30px;
  height: 3px;
  margin: 5px 0;
  background-color: var(--color-black);
  transition: all;
}
nav .nav-button-menuClose {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 0;
}
nav .nav-button-menuClose span {
  color: var(--color-black);
  font-size: 1rem;
  transition: all;
}
nav .nav-button-toggle .menuOpen-line  {
  opacity: 0;
}
nav .nav-button-toggle .nav-button-menuClose { 
    opacity: 1; 
} 
<nav>
<div class="nav-button-menuOpen">
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
</div>
<div class="nav-button-menuClose">
  <span>x</span>
</div>
</nav>

3 Answers

This is because you are adding nav-button-toggle class to the .nav-button-menuClose element, but in your css selector you trying apply opacity if this element's parent has nav-button-toggle class:

nav .nav-button-toggle .nav-button-menuClose { (basically you have an extra space between classes in this selector)

const mobileMenuOpenAndClose = () => {
    const menuOpenButton = document.querySelector('.nav-button-menuOpen');
    const menuCloseButton= document.querySelector('.nav-button-menuClose');
    const navMobileMenu = document.querySelector('.nav-mobileMenu');
    const navMobileMenuLinks = document.querySelectorAll('.nav-mobileMenu-links');

    menuOpenButton.addEventListener('click', () => {
      console.log('menuButtonOpen clicked');
      menuOpenButton.classList.toggle('nav-button-toggle');
      menuCloseButton.classList.toggle('nav-button-toggle');
    });

    menuCloseButton.addEventListener('click', () => {
      console.log('menuButtonClose clicked')
      menuCloseButton.classList.toggle('nav-button-toggle');
      menuOpenButton.classList.toggle('nav-button-toggle');
    });
};

mobileMenuOpenAndClose();
/* Hamburger Menu */
nav .nav-button-menuOpen {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 1;
}
nav .nav-button-menuOpen div {
  width: 30px;
  height: 30px;
  height: 3px;
  margin: 5px 0;
  background-color: var(--color-black);
  transition: all;
}
nav .nav-button-menuClose {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 0;
}
nav .nav-button-menuClose span {
  color: var(--color-black);
  font-size: 1rem;
  transition: all;
}
nav .nav-button-toggle .menuOpen-line  {
  opacity: 0;
}
nav .nav-button-toggle.nav-button-menuClose { 
  opacity: 1; 
}
<nav>
<div class="nav-button-menuOpen">
  <div class="menuOpen-line">menu1</div>
  <div class="menuOpen-line">menu2</div>
  <div class="menuOpen-line">menu3</div>
</div>
<div class="nav-button-menuClose">
  <span>x</span>
</div>
</nav>

As of animation, since you are only changing opacity, there nothing else to animate as there are no other changes applied to the style between the states.

Writing the css with the space in the middle like this: .nav-button-toggle .nav-button-menuClose applys the style to the elements children. Write .nav-button-toggle.nav-button-menuClose without the space in the middle to apply it to itself.

const mobileMenuOpenAndClose = () => {
    const menuOpenButton = document.querySelector('.nav-button-menuOpen');
    const menuCloseButton= document.querySelector('.nav-button-menuClose');
    const navMobileMenu = document.querySelector('.nav-mobileMenu');
    const navMobileMenuLinks = document.querySelectorAll('.nav-mobileMenu-links');

    menuOpenButton.addEventListener('click', () => {
      console.log('menuButtonOpen clicked');
      menuOpenButton.classList.toggle('nav-button-toggle');
      menuCloseButton.classList.toggle('nav-button-toggle');
    });

    menuCloseButton.addEventListener('click', () => {
      console.log('menuButtonClose clicked')
      menuCloseButton.classList.toggle('nav-button-toggle');
      menuOpenButton.classList.toggle('nav-button-toggle');
    });
};

mobileMenuOpenAndClose();
/* Hamburger Menu */
nav .nav-button-menuOpen {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 1;
}
nav .nav-button-menuOpen div {
  width: 30px;
  height: 30px;
  height: 3px;
  margin: 5px 0;
  background-color: black;
  transition: all;
}
nav .nav-button-menuClose {
  justify-self: end;
  cursor: pointer;
  display: inline-block;
  opacity: 0;
}
nav .nav-button-menuClose span {
  color: black;
  font-size: 1rem;
  transition: all;
}
nav .nav-button-toggle .menuOpen-line  {
  opacity: 0;
}
nav .nav-button-toggle.nav-button-menuClose { 
    opacity: 1; 
}
<nav>
<div class="nav-button-menuOpen">
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
</div>
<div class="nav-button-menuClose">
  <span>x</span>
</div>
</nav>

For the animation part I would recommand you to use the CSS @keyframes Rule (https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp) instead of transiton. This way you can easily apply a hide class with display: none; to the hidden elements, and if you remove the class, the animation gets triggered.

const mobileMenuOpenAndClose = () => {
    const menuOpenButton = document.querySelector('.nav-button-menuOpen');
    const menuCloseButton= document.querySelector('.nav-button-menuClose');
    const navMobileMenu = document.querySelector('.nav-mobileMenu');
    const navMobileMenuLinks = document.querySelectorAll('.nav-mobileMenu-links');

    menuOpenButton.addEventListener('click', () => {
      console.log('menuButtonOpen clicked');
      menuOpenButton.classList.toggle('hidden');
      menuCloseButton.classList.toggle('hidden');
    });

    menuCloseButton.addEventListener('click', () => {
      console.log('menuButtonClose clicked')
      menuCloseButton.classList.toggle('hidden');
      menuOpenButton.classList.toggle('hidden');
    });
};

mobileMenuOpenAndClose();
/* Hamburger Menu */
.nav-button-menuOpen {
  cursor: pointer;
  display: inline-block;
  animation: fadeIn 0.2s;
}
.nav-button-menuOpen div {
  width: 30px;
  height: 30px;
  height: 3px;
  margin: 5px 0;
  background-color: black;
}
.nav-button-menuClose {
  cursor: pointer;
  display: inline-block;
  animation: fadeIn 0.2s;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
}

@keyframes fadeIn {
  from { opacity: 0 }
  to { opacity: 100 }
}

.nav-button-menuClose span {
  color: black;
  font-size: 2rem;
}

.hidden {
  display: none
}
<div class="nav-button-menuOpen">
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
  <div class="menuOpen-line"></div>
</div>
<div class="nav-button-menuClose hidden">
  <span>x</span>
</div>

You can achieve with single .nav-menu-toggle element to open and close menu with single click event of JavaScript.

document.querySelector('.nav-menu-toggle').addEventListener('click', (t) => {
    t.target.classList.toggle('active');
});
/* Hamburger Menu */
:root{
    --color-black : #000
}
nav{
    background-color: #f2f2f2;
    padding: 20px;
}
nav .nav-menu-toggle {
    width: 30px;
    height: 30px;
    display: block;
    cursor: pointer;
    overflow: hidden;
}
nav .nav-menu-toggle div{
    width: 100%;
    height: 3px;
    margin-bottom: 10px;
    background-color: #000;
    transition: 0.5s transform cubic-bezier(0.075, 0.82, 0.165, 1);
    pointer-events: none;
}
nav .nav-menu-toggle.active div{
    background-color: #ff0000;
}
nav .nav-menu-toggle.active div:nth-child(1){
    transform: rotate(-45deg);
    transform-origin: 110% 9px;
}
nav .nav-menu-toggle.active div:nth-child(2){
    transform: scale(0);
}
nav .nav-menu-toggle.active div:nth-child(3){
    transform: rotate(45deg);
    transform-origin: 95% -4.5px;
}
<nav>
  <div class="nav-menu-toggle">
    <div></div>
    <div></div>
    <div></div>
  </div>
</nav>

Related