如何使用纯JavaScript在下拉菜单中构建可折叠选项

5

我正在尝试构建一个自定义下拉框,其中包含可折叠的选项。

下拉框中的每个选项都将有子选项,选择子选项后将给出相应的值。

在抽象层面上,下拉框应如下所示:

enter image description here

考虑以上想法,我已经通过一个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>

但我的代码片段与我需要的不太相似,而且我在 CSS 方面经验不足。 请问有谁能帮我用流畅的 CSS 实现所需的效果?

PS:我没有使用 jQuery。


请查看此问题:https://dev59.com/rnnZa4cB1Zd3GeqPuMXN#20325486 - kelvin
嗨,Kevin,我不想使用jQuery,而且状态似乎没有保留。 - Onera
1
你可以使用HTML元素<details>,这样就不需要使用JS了。 - Mark Eriksson
1个回答

2

根据您提供的图片,我对您的代码进行了大量修改,请看一下。

您可以从 .drop-box 中删除.active,这样菜单将在开始时折叠。

添加了.drop-box,包围按钮和菜单元素,并使用position: relative;使您的下拉菜单使用position: absolute;将依赖于父位置。

.drop-button:after, .link:before 这些都是箭头,它们在 .active 类上旋转,正如您所见。

更新

现在JS支持点击元素drop-box以外的区域并关闭它(移除.active)。尽管您可以添加多个像我的示例中那样的结构.drop-box元素,并且它们都将分别工作。

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>


如果您需要一些解释或改进 - 只需告诉我。 - focus.style
实际上,我们在这里漏掉了一件事,下拉菜单只有在选择项目时才会关闭,但理想情况下,当用户在下拉菜单外的任何地方单击时,我们也应该将其关闭。 - Onera
还可以修改它,使得每次只有一个组处于展开状态,其他组都处于折叠状态吗? - Onera
感谢您帮助改进我的回答 :) 完全重写了代码,使它更短、更易读和更容易实现。现在支持多个下拉框。 - focus.style

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接