Vertical menu collapsible

by Plippie Plop

HTML

<nav id="list">
<ul class="menu">
  <li class="first-menu"><a>Item 1</a></li>
  <li class="first-menu"><a>Item 2</a>
    <ul>
      <li><a>item 2 sub 1</a></li>
      <li><a>Item 2 sub 2</a></li>
    </ul>
  </li>
  <li class="first-menu"><a>Item 3</a>
    <ul>
      <li><a>item 3 sub 1</a></li>
      <li><a>Item 3 sub 2</a>
        <ul>
          <li><a>Item 2 sub-sub 1</a></li>
          <li><a>Item 2 sub-sub 2</a></li>
        </ul>
      </li>
       <li><a>Item 3 sub 3</a>
        <li><a>Item 3 sub 4</a>
         <li><a>Item 3 sub 5</a>
          <li><a>Item 3 sub 6</a>
    </ul>
  </li>
   <li class="first-menu"><a>Item 4</a>
    <ul>
      <li><a>item 4 sub 1</a></li>
      <li><a>Item 4 sub 2</a>
        <ul>
          <li><a>Item 4 sub-sub 1</a></li>
          <li><a>Item 4 sub-sub 2</a></li>
        </ul>
      </li>
    </ul>
  </li>

</ul>
</nav>

CSS

@import url('https://fonts.googleapis.com/css2?family=Oswald&display=swap');

:root {
  --menu-bg: rgb(250, 250, 250);
  --menu-item-bg: rgb(255, 255, 255);
  --menu-item-hover-bg: rgb(242, 242, 245);
  --menu-item-active-bg: rgb(124 192 251);
  --menu-item-active-hover-bg: rgb(171 214 251);  
  --menu-border-radius: 0.25rem;
}

* {
  box-sizing: border-box;
}

::-webkit-scrollbar {
  width: 0.2rem;
}

::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0);
}

::-webkit-scrollbar-thumb {
  background: rgba(100, 100, 100, 0.4);
  border-radius: 2px;
}

::-webkit-scrollbar-thumb:hover {
  background: rgba(100, 100, 100, 0.8);
}


ul,
li {
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
  outline: none;
  overflow: hidden;
}

#list {
  display: flex;
  position: relative;
  width: fit-content;
  font-family: "Oswald";
  background-color: lightgrey;
  padding: 0.2rem;
  border-radius: var(--menu-border-radius);
  min-width:200px;
  
}

.menu{
 width:100%;
 }

.menu ul { 
  max-height: 0;
  transition: max-height 0.2s;
  height: auto;
 

}

.menu li.open>ul {
  transition: max-height 0.2s;
  max-height: 15em;
  overflow-y: auto;
  border-bottom: 1px solid rgba(0, 0, 0, 0.3);
  border-top: 1px solid rgba(0, 0, 0, 0.3);
}


.menu a {
  display: flex;
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  padding: 0.5rem 1.5rem 0.5rem 0.5rem; 
  cursor: pointer;
  position: relative;
  background-color: var(--menu-item-bg);
}

.menu a:hover {
  background-color: var(--menu-item-hover-bg);
}

.menu li a.active {
background-color: var(--menu-item-active-bg);
}

.menu a.active:hover {
background-color: var(--menu-item-active-bg);
}

.menu .has-sub > ul> li> a{
  padding-left:1rem;
}
.menu .has-sub .has-sub > ul >li> a{
  padding-left:2rem;
}

.menu li:last-of-type > a{
  border-bottom:0;
}

.menu>.first-menu:first-of-type>a{
  border-radius: var(--border-radius) var(--border-radius) 0 0;
}

.menu>.first-menu:last-of-type>a, 
.menu>.first-menu:last-of-type...

JavaScript

const list = $("#list");


list.find('a').each(function(idx, elm) {
  console.log(idx, elm);
  $(elm).closest('li').attr('data-menu-index', idx);

  if ($(elm).closest('li').children('ul').length) {
    $(elm).closest('li').addClass('has-sub');
  }

});

list.find('.active').parents('li').addClass('open').find('> a').addClass('active');

list.on('click', 'a', function(e) {
  let $this = $(this);

  if (!$this.hasClass('active')) {
  console.log("not active");
  
  list.find('.open').removeClass('open');
  list.find('.active').removeClass('active');
  
    $this.addClass('active').parents('li').addClass('open').find('> a').addClass('active');
  } else {
  console.log("IS active");
    $this.removeClass('active').closest('li').removeClass('open');
    $this.children('li').removeClass('open');
  }

});