Search code examples
javascripthtmlcssdropdown

How to build a collapsible options inside a dropdown with plain JavaScript


I am trying to build a custom drop-down having collapsible options. Each option inside the drop-down will have sub options which when selected will give me the value.

On an abstract, the drop-down should look like below:

enter image description here

Considering above idea, I have tried my approach through a fiddle.

function myFunction() {
  var x = document.getElementById("myDIV");
  if (x.style.display === "none") {
    x.style.display = "block";
  } else {
    x.style.display = "none";
  }
}
.main-div {
  display: inline-block;
  padding: 15px;
  width: 180px;
  cursor:pointer;
   border: 1px solid salmon;
}

.inner-div {
  position: absolute;
  width: 210px;
  top: 58px;
  left: 8px;
  height: 300px;
  border: 1px solid salmon;
}
.inner-div > ul {
  list-style-type: none;
  border-bottom: 1px solid salmon;
  margin: 5px;
  padding: 10px;
}
.inner-div > ul > span {
  display: inline;
}
.inner-div .acc-input {
  position: absolute;
  opacity: 0;
}
.inner-div .acc-input:checked ~ .acc-sub-cat {
  display: block;
}
.inner-div .acc-sub-cat {
  display: none;
  overflow: hidden;
}
<div class="main-div" onclick="myFunction()"> Select Items
</div>

<div class="inner-div" id="myDIV">
	<ul>
		<li>
			<input class="acc-input" type="checkbox" id="group-1">
			<label for="group-1"><span>Group 1</span></label>
			<ul class="acc-sub-cat">
				<li><a><span>Item 1</span></a></li>
				<li><a><span>Item 2</span></a></li>
			</ul>
		</li>
	</ul>
</div>

But my snippet does not resemble the way I need it and I am not much experienced in CSS. Can anyone help me to arrive at the desired figure with smooth CSS, please?

PS: I am not using jquery


Solution

  • Rewrited your code a lot according to your picture. Please, take a look.

    You can remove .active from .drop-box so the menu will be collapsed at a start.

    Added .drop-box around button and menu elements, with position: relative; so your dropdown menu with position: absolute; will depends on the parent position.

    .drop-button:after, .link:before these are arrows, they they are rotating on .active class, as you can see.

    UPDATED

    Now the JS supports clicking outside of the element drop-box and closing it (removes .active). Although you can add multiple .drop-box element with structure like in my example, and they all will work separately.

    for (let dropbox of document.querySelectorAll('.drop-box')) {
      let dropButton = dropbox.querySelector(".drop-button");
      let dropMenu = dropbox.querySelector(".drop-menu");
    
      document.body.addEventListener("click", function(e) {
        let target = e.target || e.srcElement;
        if (target !== dropbox && !isChildOf(target, dropbox)) {
          dropbox.classList.remove("active");
        }
      }, false);
    
      function isChildOf(child, parent) {
        if (child.parentNode === parent) {
          return true;
        } else if (child.parentNode === null) {
          return false;
        } else {
          return isChildOf(child.parentNode, parent);
        }
      }
    
      dropButton.addEventListener("click", function(e) {
        dropbox.classList.toggle("active");
        for (let link of dropMenu.querySelectorAll('.link')) {
          link.classList.remove("active");
        }    
      }, false);
    
      let xx = 0;
      for (let link of dropMenu.querySelectorAll('.link')) {
        (function(index){
          link.addEventListener("click", function() {
            let yy = 0;
            for (let links of dropMenu.querySelectorAll('.link')) {
              if (index !== yy) {
                links.classList.remove("active");
              }          
              yy++
            }
            this.classList.toggle("active");
          })
        })(xx);
        xx++;
      }
    
    }
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }
    
    .drop-box {
      position: relative;
      display: inline-block;
    }
    
    .drop-button {
      display: inline-block;
      padding: 10px;
      cursor: pointer;
      border: 1px solid #000;
      background: #fff;
    }
    
    .drop-button:after,
    .link:before {
      content: '';
      border: 5px solid transparent;
      border-top-color: #000;
      box-sizing: border-box;
      display: inline-block;
      margin: 0 3px;
      transform: rotate(-90deg);
    }
    
    .drop-box.active .drop-button:after,
    .link.active:before {
      transform: rotate(0deg);
    }
    
    .drop-menu {
      display: none;
      position: absolute;
      width: 210px;
      top: 100%;
      left: 0px;
      border: 1px solid #000;
      background: #fff;
    }
    
    .drop-box.active .drop-menu {
      display: block;
    }
    
    .drop-menu .link {
      padding: 10px;
      cursor: pointer;
    }
    
    .drop-menu .box {
      display: none;
      padding: 0 10px 10px 10px;
    }
    
    .drop-menu .link.active+.box {
      display: block;
    }
    
    .drop-menu .box label {
      display: block;
    }
    <div class="drop-box active">
      <div class="drop-button">Select Items 1</div>
      <div class="drop-menu">
        <div class="link-box">
          <div class="link">Group 1</div>
          <div class="box">
            <label><input type="checkbox" id="box-1"><span>Box 1</span></label>
            <label><input type="checkbox" id="box-2"><span>Box 2</span></label>
          </div>
        </div>
        <div class="link-box">
          <div class="link">Group 2</div>
          <div class="box">
            <label><input type="checkbox" id="box-3"><span>Box 3</span></label>
            <label><input type="checkbox" id="box-4"><span>Box 4</span></label>
          </div>
        </div>
      </div>
    </div>
    
    <div class="drop-box">
      <div class="drop-button">Select Items 2</div>
      <div class="drop-menu ">
        <div class="link-box">
          <div class="link">Group 3</div>
          <div class="box">
            <label><input type="checkbox" id="box-5"><span>Box 5</span></label>
            <label><input type="checkbox" id="box-6"><span>Box 6</span></label>
          </div>
        </div>
        <div class="link-box">
          <div class="link">Group 4</div>
          <div class="box">
            <label><input type="checkbox" id="box-7"><span>Box 7</span></label>
            <label><input type="checkbox" id="box-8"><span>Box 8</span></label>
          </div>
        </div>
      </div>
    </div>