Home > Mobile >  Javascript Menu : Click on to extend & Click off to shorten
Javascript Menu : Click on to extend & Click off to shorten

Time:02-21

I created a menu for a web project, but I have a problem with the javascript side. I would like to extend the div container if it's clicked. And vice versa, I would like to shorten it if I click everywhere except on it. Actually, the program is able to shorten and extend the div menu but I can't manage the events :

  • if div is small -> extend it by clicking on it
  • if div is big -> shorten it by clicking everywhere except on it

There is my program :

const menu = document.querySelector('.wrapper')
    const offClick = () => {
        menu.classList.toggle('active')
        document.removeEventListener('click', offClick)
    }
    const handleClick = (e) => {
        e.stopPropagation()
        menu.classList.toggle('.active')
        if (menu.classList.contains('.active')){
            document.addEventListener('click', offClick)
        }
    }
menu.addEventListener('click', handleClick)
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

 body{
   display: flex;
   justify-content: center;
   align-items: center;
   min-height: 100vh;
   background-color: #10131c;

 }

 .wrapper{
   position: absolute;
   width: 105px;
   height: 105px;
   background-color: #212532;
   border-radius: 10px;
   cursor: pointer;
   display: flex;
   justify-content: center;
   align-items: center;
   transition: 0.5s;
   transition-delay: 0.8s;

 }

 .wrapper.active{
  width: 300px;
  height: 300px;
  transition-delay: 0s;
 }

 .wrapper span{
   position: absolute;
   width: 10.5px;
   height: 10.5px;
   display: flex;
   justify-content: center;
   align-items: center;
   background-color: #fff;
   border-radius: 50%;
   transform: translate(calc(18px * var(--x)), calc(18px * var(--y)));
   transition: transform 0.5s, width 0.5s, height 0.5s;
   background-color: 0.5s;
   transition-delay: calc(0.1s * var(--i));
 }

 .wrapper.active span{
  width: 67.5px;
  height: 67.5px;
  background-color: #333849;
  transform: translate(calc(90px * var(--x)), calc(90px * var(--y)))

 }

 .wrapper span ion-icon{
  transition: 0.5s;
  font-size: 0em
}

.wrapper.active span ion-icon{
  font-size: 2.025em;
  color: #fff;
}

.wrapper.active span:hover ion-icon{
  color: #d33d32;
  filter: drop-shadow(0 0 3px #d33d32) 

}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSite</title>
    <link rel="stylesheet" href="styles/style.css" type="text/css">
</head>
<body >
    <div >
        <span style = "--i:0;--x:-1;--y:0;"><ion-icon name="code-outline"></ion-icon></span>
        <span style = "--i:1;--x:1;--y:0;"><ion-icon name="moon-outline"></ion-icon></span>
        <span style = "--i:2;--x:0;--y:-1;"><ion-icon name="bulb-outline"></ion-icon></span>
        <span style = "--i:3;--x:0;--y:1;"><ion-icon name="chatbubbles-outline"></ion-icon></span>
        <span style = "--i:4;--x:1;--y:1;"><ion-icon name="duplicate-outline"></ion-icon></span>
        <span style = "--i:5;--x:-1;--y:-1;"><ion-icon name="duplicate-outline"></ion-icon></span>
        <span style = "--i:6;--x:0;--y:0;"><ion-icon name="ribbon-outline"></ion-icon></ion-icon></span>
        <span style = "--i:7;--x:-1;--y:1;"><ion-icon name="rocket-outline"></ion-icon></span>
        <span style = "--i:8;--x:1;--y:-1;"><ion-icon name="stats-chart-outline"></ion-icon></span>
    </div>
    
    <script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"></script>
    <script nomodule src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"></script>
    <script src="script/script.js"></script>
</body>
</html>

Thank you for your help !

CodePudding user response:

Firstly, note that amending event handlers within an event handler is normally a code smell. There is usually a much better way to do what you need to achieve.

In this case you need to attach each of your event handlers just the once. handleClick() should be attached to the .wrapper so that it expands when clicked, and offClick() should be attached to the document so that it contracts the .wrapper.

const menu = document.querySelector('.wrapper');
const offClick = () => menu.classList.remove('active');
const handleClick = (e) => {
  e.stopPropagation();
  menu.classList.add('active'); // or .toggle('active')
}

menu.addEventListener('click', handleClick);
document.addEventListener('click', offClick);
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #10131c;
}

.wrapper {
  position: absolute;
  width: 105px;
  height: 105px;
  background-color: #212532;
  border-radius: 10px;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: 0.5s;
  transition-delay: 0.8s;
}

.wrapper.active {
  width: 300px;
  height: 300px;
  transition-delay: 0s;
}

.wrapper span {
  position: absolute;
  width: 10.5px;
  height: 10.5px;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #fff;
  border-radius: 50%;
  transform: translate(calc(18px * var(--x)), calc(18px * var(--y)));
  transition: transform 0.5s, width 0.5s, height 0.5s;
  background-color: 0.5s;
  transition-delay: calc(0.1s * var(--i));
}

.wrapper.active span {
  width: 67.5px;
  height: 67.5px;
  background-color: #333849;
  transform: translate(calc(90px * var(--x)), calc(90px * var(--y)))
}

.wrapper span ion-icon {
  transition: 0.5s;
  font-size: 0em
}

.wrapper.active span ion-icon {
  font-size: 2.025em;
  color: #fff;
}

.wrapper.active span:hover ion-icon {
  color: #d33d32;
  filter: drop-shadow(0 0 3px #d33d32)
}
<body >
  <div >
    <span style="--i:0;--x:-1;--y:0;"><ion-icon name="code-outline"></ion-icon></span>
    <span style="--i:1;--x:1;--y:0;"><ion-icon name="moon-outline"></ion-icon></span>
    <span style="--i:2;--x:0;--y:-1;"><ion-icon name="bulb-outline"></ion-icon></span>
    <span style="--i:3;--x:0;--y:1;"><ion-icon name="chatbubbles-outline"></ion-icon></span>
    <span style="--i:4;--x:1;--y:1;"><ion-icon name="duplicate-outline"></ion-icon></span>
    <span style="--i:5;--x:-1;--y:-1;"><ion-icon name="duplicate-outline"></ion-icon></span>
    <span style="--i:6;--x:0;--y:0;"><ion-icon name="ribbon-outline"></ion-icon></span>
    <span style="--i:7;--x:-1;--y:1;"><ion-icon name="rocket-outline"></ion-icon></span>
    <span style="--i:8;--x:1;--y:-1;"><ion-icon name="stats-chart-outline"></ion-icon></span>
  </div>

  <script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"></script>
</body>

CodePudding user response:

First of all it looks really nice :)

As far as the javascript code you could keep it simple by adding the event listener on the whole body and checking the target of your click event has a parent with a 'wrapper' class, by using the closest method.

document.addEventListener('click',(event)=>{
    if(event.target.closest(".wrapper")){
        menu.classList.add('active')
    }else{
        menu.classList.remove('active')
    }
})
  • Related